Search code examples
c#.netunit-testingtask-parallel-libraryvs-unit-testing-framework

How to unit test for Task parellelism


I have a class in .NET which creates and starts a new System.Threading.Tasks.Task as follows:

public class ScheduledTask
{
    private IFoo _foo;

    public ScheduledTask(IFoo foo)
    {
        _foo = foo;
    }

    public void Start()
    {           
        _task = new Task(() => Run());
        _task.Start();
    }

    public void Stop(TimeSpan timeout)
    {
        var taskCompletedNormally = _task.Wait(timeout);
        if (taskCompletedNormally)
        {                
            _task.Dispose();
            _task = null;                
        }
    }

    private void Run(){ // Do some work}
}

How do I unit test the ScheduledTask.Start and ScheduledTask.Stop methods in C#.Net? Which are the frameworks available for such unit tests and which are the best practices for unit testing threading (or task parallelism)?


Solution

  • Your class is doing to much. Start/stop is a generic function that should be in its own class.

    public class StartStopTask
    {
        private readonly Action _action;
    
        public StartStopTask(Action action)
        {
            _action = action;
        }
    
        public void Start()
        {           
            _task = new Task(_action);
            _task.Start();
        }
        ...
    }
    

    This class is easy to unit test.

    bool worked = false;
    var startstop = new StartStopTask(() => { worked = true });
    startstop.Start();
    startstop.Stop(new TimeSpan(0,0,0,10));
    Assert.That(worked, Is.True);
    

    Your other classes then uses the StartStopTask to do its work.

    Either derive

    public class ScheduledTask : StartStopTask
    {
        private IFoo _foo;
    
        public ScheduledTask(IFoo foo)
            : base(() => Run())
        {
            _foo = foo;
        }
    
        private void Run(){ // Do some work }
    }
    

    Or just delegate the work

    public class ScheduledTask
    {
        private IFoo _foo;
        private readonly StartStopTask _startstop;
    
        public ScheduledTask(IFoo foo)
        {
            _foo = foo;
            _startstop = new StartStopTask(() => Run());
        }
    
        public void Start()
        {           
            _startstop.Start();
        }
    
        public void Stop(TimeSpan timeout)
        {
            _startstop.Stop(timeout);
        }
    
        private void Run(){ // Do some work }
    }
    

    Even better would be to just let Run be a public method and let the caller decide how it should be run.