【发布时间】:2012-12-09 03:23:03
【问题描述】:
假设我有以下类和它所依赖的接口:
public class MyController
{
private IRepository _repository;
public MyController(IRepository repository)
{
_repository = repository;
}
public async Task MethodUnderTest(int someId)
{
var o = await _repository.FindById(someId);
// update o
await _repository.Commit();
}
}
public interface IRepository
{
Task Commit();
}
当我对该方法进行单元测试时,我可以执行以下操作(使用 xUnit 和 Rhino Mocks):
[Fact]
public async Task MyTest()
{
IRepository repositoryStub = MockRepository.GenerateStub<IRepository>();
MyController controller = new MyController(repositoryStub);
await controller.MethodUnderTest(1);
}
这会失败并出现 System.NullReferenceException : Object reference not set to an instance of an object。
使用以下 StackTrace:
UnitTest.MyController.<MethodUnderTest>d__0.MoveNext() in
\UnitTest\Class1.cs:line 35
--- End of stack trace from previous location where exception was thrown ---
at System.Runtime.CompilerServices.TaskAwaiter.ThrowForNonSuccess(Task task)
at System.Runtime.CompilerServices.TaskAwaiter.HandleNonSuccessAndDebuggerNotification(Task task)
at System.Runtime.CompilerServices.TaskAwaiter.GetResult()
at \UnitTest\Class1.cs:line 20
由于Commit() 返回null 并且为async/await 生成的状态机在null 上调用MoveNext() 而发生此错误是否正确?
我可以通过以下方式解决此问题:
repositoryStub.Expect(r => r.Commit()).Return(Task.FromResult<bool>(true);
但这感觉有点奇怪..我可以使用任何T 来代替FromResult<T> 并且测试将运行。我找不到将返回非泛型 Task 的 FromResult 方法。
T 使用什么重要吗?还是我应该以其他方式解决这个问题?
【问题讨论】:
-
无论您使用什么类型的
T,因为Task<T>扩展了Task。您可以改用Task.Delay(0)。如果您不关心任务本身,您可以将MethodUnderTest更改为直接返回_repository.Commit(),而不是创建异步方法。 -
Repository.Commit() 使用 Entity Frameworks 6 新的异步功能来保存对数据库的更改。我从 ASP.NET MVC 应用程序中使用它。直接返回 _repository.Commit() 是什么意思?
-
我的意思是
MethodUnderTest可能是return _repository.Commit();如果你删除了async修饰符,所以如果你没有存根_repository.Commit()它只会在你的测试中返回一个空任务。跨度> -
OK.. 这个例子有点小。我在 MethodUnderTest 中有多个等待,所以我不能只为 Commit() 删除 async 关键字
标签: c# unit-testing task-parallel-library async-await