Unit testing code that uses a BackgroundWorker

The BackgroundWorker class is a fantastically simple way of firing off long-running tasks without the complexities associated with threading. However, tests that exercise code that uses a BackgroundWorker may not behave as desired. Unit tests run synchronously, and will not wait for the BackgroundWorker to complete. This article shows an approach which resolves this limitation.

[TestFixture]
    public class TestClass
    {
        [Test]
        public void TestLongRunningOperationCompletesSuccessfullyBadImplementation()
        {
            SomeLongRunningOperation task = new SomeLongRunningOperation();
            task.DoStuff();
            Assert.AreEqual(true, task.IsHit);
        }
    }

    public class SomeLongRunningOperation
    {
        private bool _isHit;

        public bool IsHit
        {
            get { return _isHit; }
        }

        public void DoStuff()
        {
            BackgroundWorker worker = new BackgroundWorker();
            worker.DoWork += delegate
            {
                _isHit = true;
            };
            worker.RunWorkerAsync();
        }
    }

This test will never pass as the unit test runner runs synchronously, so it will not wait for the background work to complete, and the assertion will never be met. Of course, we could put a Thread.Sleep in the test to wait for the task to complete, but this is brittle and as choosing an appropriate time is difficult it will make the test run more slowly.

Instead, we can create a wrapper around the BackgroundWorker class, and an implementation that runs synchronously just for testing.

public class BackgroundWorkerWrapper : IBackgroundWorker
    {
        readonly BackgroundWorker _worker = new BackgroundWorker();

        public void RunWorkerAsync()
        {
            _worker.RunWorkerAsync();
        }

        public void RunWorkerAsync(object args)
        {
            _worker.RunWorkerAsync(args);
        }

        public event RunWorkerCompletedEventHandler RunWorkerCompleted
        {
            add { _worker.RunWorkerCompleted += value; }
            remove { _worker.RunWorkerCompleted -= value; }
        }

        public event DoWorkEventHandler DoWork
        {
            add { _worker.DoWork += value; }
            remove { _worker.DoWork -= value; }
        }

        public bool IsBusy
        {
            get { return _worker.IsBusy; }
        }
    }

    public class SynchronousBackgroundWorker : IBackgroundWorker
    {
        public void RunWorkerAsync()
        {
            DoWorkEventArgs args = new DoWorkEventArgs(null);
            RunWorkerSync(args);
        }

        private void RunWorkerSync(DoWorkEventArgs args)
        {
            DoWork.Invoke(this, args);
            RunWorkerCompleted.Invoke(this, new RunWorkerCompletedEventArgs(args.Result, null, false));
        }

        public void RunWorkerAsync(object arguments)
        {
            DoWorkEventArgs args = new DoWorkEventArgs(arguments);
            RunWorkerSync(args);
        }

        public event RunWorkerCompletedEventHandler RunWorkerCompleted = delegate { };

        public event DoWorkEventHandler DoWork = delegate { };

        public bool IsBusy
        {
            get { return false; }
        }
    }

    public class BackgroundWorkerFactory : IBackgroundWorkerFactory
    {
        public IBackgroundWorker GetWorker()
        {
            return IoC.Resolve();
        }
    }

    public class SynchronousBackgroundWorkerFactory : IBackgroundWorkerFactory
    {
        public IBackgroundWorker GetWorker()
        {
            return new SynchronousBackgroundWorker ();
        }
    }

    public interface IBackgroundWorkerFactory
    {
        IBackgroundWorker GetWorker();
    }

    public interface IBackgroundWorker
    {
        void RunWorkerAsync();
        void RunWorkerAsync(object arguments);
        event RunWorkerCompletedEventHandler RunWorkerCompleted;
        event DoWorkEventHandler DoWork;
        bool IsBusy { get;}
    }

Now we can modify the class that performs the long-running operation to have a backgroundworker factory injected. The production code can pull out the “real” factory via the DI container.

[TestFixture]
    public class TestClass
    {
        [Test]
        public void TestLongRunningOperationCompletesSuccessfully()
        {
            SomeLongRunningOperation task = new SomeLongRunningOperation(new SynchronousBackgroundWorkerFactory());
            task.DoStuff();
            Assert.AreEqual(true, task.IsHit);
        }
    }

    public class SomeLongRunningOperation
    {
        private readonly IBackgroundWorkerFactory _backgroundWorkerFactory;

        private bool _isHit;

        public SomeLongRunningOperation(IBackgroundWorkerFactory backgroundWorkerFactory)
        {
            _backgroundWorkerFactory = backgroundWorkerFactory;
        }

        public bool IsHit
        {
            get { return _isHit; }
        }

        public void DoStuff()
        {
            IBackgroundWorker worker =  _backgroundWorkerFactory.GetWorker();

            worker.DoWork += delegate
                                 {
                                     _isHit = true;
                                 };
            worker.RunWorkerAsync();
        }
    }
Digg This
Reddit This
Stumble Now!
Buzz This
Vote on DZone
Share on Facebook
Bookmark this on Delicious
Kick It on DotNetKicks.com
Shout it
Share on LinkedIn
Bookmark this on Technorati
Post on Twitter
Google Buzz (aka. Google Reader)
This entry was posted in Uncategorized. Bookmark the permalink.