我正在尝试为我的代码添加单元测试,我在TPL中使用Task
将值更新到数据库中。对于单元测试,我使用NUnit
和Moq
。以下是我项目中的一些代码片段。
*//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());
这很有效。但是当我添加ContinueWith
这样的服务方法调用时......
_ServiceFacade.SynchronizeDataset(DATASET_NAME).ContinueWith(t =>
{
if (t.IsFaulted)
{
//do something
}
});
此测试代码无效。测试失败并显示此错误...
System.NullReferenceException:对象引用未设置为 对象的实例
堆栈跟踪:
atPresenters.UnitTests.DeviceCategoryPresenterTest.InitializeView_Called ()[0x00241]在DeviceCategoryPresenterTest.cs中:56 at(wrapper managed-to-native)System.Reflection.MonoMethod:InternalInvoke (System.Reflection.MonoMethod,对象,对象[],System.Exception的&安培) 在System.Reflection.MonoMethod.Invoke(System.Object obj,System.Reflection.BindingFlags invokeAttr,System.Reflection.Binder) binder,System.Object []参数,System.Globalization.CultureInfo 文化)[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
我不知道如何解决它。请帮忙。提前谢谢。
答案 0 :(得分:2)
这里的事实是您通过传递有效任务而不是It.IsAny<Task>
来跳过延续。一个例子是做这样的事情
.NET&lt; V4.6 强>
mock_IServicesFacade
.Setup(sf => sf.SynchronizeDataset(It.IsAny<string>()))
.Returns(Task.FromResult(true)))
.NET&gt; = v4.6
mock_IServicesFacade
.Setup(sf => sf.SynchronizeDataset(It.IsAny<string>()))
.Returns(Task.CompletedTask))
您甚至可以尝试使用选项TaskContinuationOptions.OnlyOnFaulted
进行延续,因为您只对IsFaulted
方案感兴趣。
请注意,您不是仅仅跳过它来测试延续部分。如果你真的想测试\验证延续部分要小心。您的逻辑似乎是服务端逻辑,因此TaskScheduler
将使用默认SynchronizationContext
并在ThreadPool
线程上安排延续。当然,这是在单元测试运行器上下文中执行的,它是相同的。基本上,即使在执行延续任务之前,您的测试也可以完成。
答案 1 :(得分:1)
在您的设置中,您设置了返回null
的功能。您已在评论中说明了这一点,It.IsAny<Task>()
会返回null
。
Setup(sf => sf.SynchronizeDataset(It.IsAny<string>()))
.Returns(It.IsAny<Task>());
所以如果我们打破这个:
_ServiceFacade.SynchronizeDataset(DATASET_NAME).ContinueWith(t =>
{
if (t.IsFaulted)
{
//do something
}
});
...等于
// 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
好像你正在编写一个不适用于你的代码的集成测试(如果你的实际代码确实假设非null为return)。如果是这种情况,我建议您将设置更改为:
Setup(sf => sf.SynchronizeDataset(It.IsAny<string>()))
.Returns(Task.CompletedTask);