Md. Tahmid Mozaffar
Md. Tahmid Mozaffar

Reputation: 1005

Unit test is failing when ContinueWith is used with System.Threading.Tasks.Task

I am trying to add unit-tests for my code where I am using Task from TPL to update values into a database. For unit-test, I am using NUnit and Moq. Here are some code snippets from my project.

*//service*
public interface IServiceFacade{
   Task SynchronizeDataset (string datasetName);
}

*//The method call I want to test*
_ServiceFacade.SynchronizeDataset(DATASET_NAME);

*//In my test, I want to verify if this method is called*
mock_IServicesFacade.Setup(sf => sf.SynchronizeDataset(It.IsAny<string>())).Returns(It.IsAny<Task>());
presenter.InitializeView();
mock_IServicesFacade.Verify(sf => sf.SynchronizeDataset(NSUserUtilStrings.DATASET_ACHIEVEMENT), Times.Once());

This is working. But when I add ContinueWith with the service method call like this...

_ServiceFacade.SynchronizeDataset(DATASET_NAME).ContinueWith(t =>
{
    if (t.IsFaulted)
    {
        //do something
    }
});

this test code is not working.Test is failed and it shows this error...

System.NullReferenceException : Object reference not set to an instance of an object

Stacktrace:

atPresenters.UnitTests.DeviceCategoryPresenterTest.InitializeView_Called () [0x00241] in DeviceCategoryPresenterTest.cs:56 at (wrapper managed-to-native) System.Reflection.MonoMethod:InternalInvoke (System.Reflection.MonoMethod,object,object[],System.Exception&) at System.Reflection.MonoMethod.Invoke (System.Object obj, System.Reflection.BindingFlags invokeAttr, System.Reflection.Binder binder, System.Object[] parameters, System.Globalization.CultureInfo culture) [0x00038] in /private/tmp/source-mono-4.8.0/bockbuild-mono-4.8.0-branch/profiles/mono-mac-xamarin/build-root/mono-x86/mcs/class/corlib/System.Reflection/MonoMethod.cs:305

and I am not sure how can I fix it. Please help. Thanks in advance.

Upvotes: 6

Views: 1650

Answers (2)

Johnny
Johnny

Reputation: 9519

The fact here is that you are skipping your continuation by passing the valid task instead of It.IsAny<Task>. The one example is to do something like this

.NET < v4.6

mock_IServicesFacade
    .Setup(sf => sf.SynchronizeDataset(It.IsAny<string>()))
    .Returns(Task.FromResult(true)))

.NET >= v4.6

mock_IServicesFacade
    .Setup(sf => sf.SynchronizeDataset(It.IsAny<string>()))
    .Returns(Task.CompletedTask))

You can even try to make your continuation with option TaskContinuationOptions.OnlyOnFaulted because you are only interested in IsFaulted scenario.

Be aware that you are not testing continuation part only skipping it. If you really want to test\verify continuation part be careful with it. It seems that your logic is service side logic so there TaskScheduler will use default SynchronizationContext and schedule continuation on the ThreadPool thread. Of course this is executed within unit test runner context which is the same. Basically your tests could finish even before continuation task is executed.

Upvotes: 4

Tewr
Tewr

Reputation: 3853

In your setup you set up the function to return null. You already stated this in a comment, It.IsAny<Task>() returns null.

Setup(sf => sf.SynchronizeDataset(It.IsAny<string>()))
    .Returns(It.IsAny<Task>());

So if we break this down:

_ServiceFacade.SynchronizeDataset(DATASET_NAME).ContinueWith(t =>
           {
               if (t.IsFaulted)
               {
                  //do something
               }
           });

... equals

// This works, but returns null, so testing anything from this point is limited.
var myNullTask = _ServiceFacade.SynchronizeDataset(DATASET_NAME);

myNullTask.ContinueWith(t => ... ); // This yields NullReferenceException
((Task)null).ContinueWith(t => ... ); // Equivalent to line above

Seems like you are writing an integration test which does not apply to your code (if your actual code does assume non-null as return). If this is the case, I suggest changing your setup to something like:

Setup(sf => sf.SynchronizeDataset(It.IsAny<string>()))
    .Returns(Task.CompletedTask);

Upvotes: 1

Related Questions