Vac*_*ano 63 .net c# unit-testing async-await
我有一个看起来像这样的方法:
private async void DoStuff(long idToLookUp)
{
IOrder order = await orderService.LookUpIdAsync(idToLookUp);
// Close the search
IsSearchShowing = false;
}
//Other stuff in case you want to see it
public DelegateCommand<long> DoLookupCommand{ get; set; }
ViewModel()
{
DoLookupCommand= new DelegateCommand<long>(DoStuff);
}
Run Code Online (Sandbox Code Playgroud)
我试图对它进行单元测试:
[TestMethod]
public void TestDoStuff()
{
//+ Arrange
myViewModel.IsSearchShowing = true;
// container is my Unity container and it setup in the init method.
container.Resolve<IOrderService>().Returns(orderService);
orderService = Substitute.For<IOrderService>();
orderService.LookUpIdAsync(Arg.Any<long>())
.Returns(new Task<IOrder>(() => null));
//+ Act
myViewModel.DoLookupCommand.Execute(0);
//+ Assert
myViewModel.IsSearchShowing.Should().BeFalse();
}
Run Code Online (Sandbox Code Playgroud)
在我完成模拟的LookUpIdAsync之前调用了我的断言.在我的正常代码中,这正是我想要的.但是对于我的单元测试,我不希望这样.
我正在使用BackgroundWorker转换为Async/Await.使用后台工作程序,这是正常运行的,因为我可以等待BackgroundWorker完成.
但似乎没有办法等待异步void方法......
我该如何对这种方法进行单元测试?
Ste*_*ary 56
你应该避免async void.仅async void用于事件处理程序.DelegateCommand是(逻辑上)一个事件处理程序,所以你可以这样做:
// Use [InternalsVisibleTo] to share internal methods with the unit test project.
internal async Task DoLookupCommandImpl(long idToLookUp)
{
IOrder order = await orderService.LookUpIdAsync(idToLookUp);
// Close the search
IsSearchShowing = false;
}
private async void DoStuff(long idToLookUp)
{
await DoLookupCommandImpl(idToLookup);
}
Run Code Online (Sandbox Code Playgroud)
并将单元测试为:
[TestMethod]
public async Task TestDoStuff()
{
//+ Arrange
myViewModel.IsSearchShowing = true;
// container is my Unity container and it setup in the init method.
container.Resolve<IOrderService>().Returns(orderService);
orderService = Substitute.For<IOrderService>();
orderService.LookUpIdAsync(Arg.Any<long>())
.Returns(new Task<IOrder>(() => null));
//+ Act
await myViewModel.DoLookupCommandImpl(0);
//+ Assert
myViewModel.IsSearchShowing.Should().BeFalse();
}
Run Code Online (Sandbox Code Playgroud)
我推荐的答案是上面的.但是如果你真的想测试一个async void方法,你可以用我的AsyncEx库来做:
[TestMethod]
public void TestDoStuff()
{
AsyncContext.Run(() =>
{
//+ Arrange
myViewModel.IsSearchShowing = true;
// container is my Unity container and it setup in the init method.
container.Resolve<IOrderService>().Returns(orderService);
orderService = Substitute.For<IOrderService>();
orderService.LookUpIdAsync(Arg.Any<long>())
.Returns(new Task<IOrder>(() => null));
//+ Act
myViewModel.DoLookupCommand.Execute(0);
});
//+ Assert
myViewModel.IsSearchShowing.Should().BeFalse();
}
Run Code Online (Sandbox Code Playgroud)
但是此解决方案会SynchronizationContext在其生命周期内更改视图模型.
Ree*_*sey 50
一种async void方法本质上是一种"发射后不管"的方法.没有办法回到完成事件(没有外部事件等).
如果您需要对此进行单元测试,我建议您将其作为一种async Task方法.然后,您可以调用Wait()结果,该结果将在方法完成时通知您.
但是,这样编写的测试方法仍然无效,因为您实际上并没有DoStuff直接测试,而是测试DelegateCommand包装它的方法.您需要直接测试此方法.
Vac*_*ano 18
我想出了一种方法来进行单元测试:
[TestMethod]
public void TestDoStuff()
{
//+ Arrange
myViewModel.IsSearchShowing = true;
// container is my Unity container and it setup in the init method.
container.Resolve<IOrderService>().Returns(orderService);
orderService = Substitute.For<IOrderService>();
var lookupTask = Task<IOrder>.Factory.StartNew(() =>
{
return new Order();
});
orderService.LookUpIdAsync(Arg.Any<long>()).Returns(lookupTask);
//+ Act
myViewModel.DoLookupCommand.Execute(0);
lookupTask.Wait();
//+ Assert
myViewModel.IsSearchShowing.Should().BeFalse();
}
Run Code Online (Sandbox Code Playgroud)
这里的关键是,因为我是单元测试,我可以替换我希望在异步调用(在我的异步void内)返回的任务.然后我确保在继续之前完成任务.
小智 5
您可以使用AutoResetEvent暂停测试方法,直到异步调用完成:
[TestMethod()]
public void Async_Test()
{
TypeToTest target = new TypeToTest();
AutoResetEvent AsyncCallComplete = new AutoResetEvent(false);
SuccessResponse SuccessResult = null;
Exception FailureResult = null;
target.AsyncMethodToTest(
(SuccessResponse response) =>
{
SuccessResult = response;
AsyncCallComplete.Set();
},
(Exception ex) =>
{
FailureResult = ex;
AsyncCallComplete.Set();
}
);
// Wait until either async results signal completion.
AsyncCallComplete.WaitOne();
Assert.AreEqual(null, FailureResult);
}
Run Code Online (Sandbox Code Playgroud)
提供的答案测试命令而不是异步方法。如上所述,您还需要另一个测试来测试该异步方法。
在遇到类似问题花了一些时间后,我发现只需同步调用即可轻松等待在单元测试中测试异步方法:
protected static void CallSync(Action target)
{
var task = new Task(target);
task.RunSynchronously();
}
Run Code Online (Sandbox Code Playgroud)
和用法:
CallSync(() => myClass.MyAsyncMethod());
Run Code Online (Sandbox Code Playgroud)
测试在这条线上等待并在结果准备好后继续,所以我们可以在之后立即断言。
| 归档时间: |
|
| 查看次数: |
82118 次 |
| 最近记录: |