Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Unit testing code that uses Task.Factory.StartNew().ContinueWith()

so I have some code

Task.Factory.StartNew(() => this.listener.Start()).ContinueWith(
                    (task) =>
                        {
                            if (task.IsCompleted)
                            {
                                this.status = WorkerStatus.Started;
                                this.RaiseStatusChanged();
                                this.LogInformationMessage("Worker Started.");
                            }
                        });

When I am testing I am mocking all the dependant objects (namley this.listener.Start()). the problem is that the test finishes executing before ContinueWith can be called. When I debug it gets called fine due to the extra delay of me stepping through code.

so how can I - from the test code in a different assembly - ensure that the code is run before my test hits its asserts?

I could just use Thread.Sleep ... but this seems like a really hacky way of doing it.

I guess I am looking for the Task version of Thread.Join.

like image 802
John Nicholas Avatar asked Nov 09 '12 13:11

John Nicholas


People also ask

What is the difference between task run () and Taskfactory StartNew () methods?

Run(action) internally uses the default TaskScheduler , which means it always offloads a task to the thread pool. StartNew(action) , on the other hand, uses the scheduler of the current thread which may not use thread pool at all!

What is StartNew?

StartNew(Action<Object>, Object, TaskCreationOptions) Creates and starts a task for the specified action delegate, state and creation options. StartNew(Action<Object>, Object, CancellationToken) Creates and starts a task for the specified action delegate, state and cancellation token.


1 Answers

Consider the following:

public class SomeClass
{
    public void Foo()
    {
        var a = new Random().Next();
    }
}

public class MyUnitTest
{
    public void MyTestMethod()
    {
        var target = new SomeClass();        
        target.Foo(); // What to assert, what is the result?..
    }
}

What is the value assigned to a? You cannot tell, unless the result is returned outside the method Foo() (as the return value, a public property, an event, etc.).

The process of "coordinating the actions of threads for a predictable outcome" is called Synchronization.

One of the easiest solutions in your case might be to return the instance of Task class and the use its Wait() method:

var task = Task.Factory.StartNew(() => Method1())
    .ContinueWith(() => Method2());

No need to wait for the first task, because ContinueWith() creates a continuation that executes asynchronously when the target Task completes (MSDN):

task.Wait();
like image 178
maximpa Avatar answered Sep 21 '22 01:09

maximpa