C# 等待 Async Void 方法调用进行单元测试
声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow
原文地址: http://stackoverflow.com/questions/14205590/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me):
StackOverFlow
Await a Async Void method call for unit testing
提问by Vaccano
I have a method that looks like this:
我有一个看起来像这样的方法:
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);
}
I am trying to unit test it like this:
我正在尝试像这样对其进行单元测试:
[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();
}
My assert is called before I get done with the mocked up LookUpIdAsync. In my normal code, that is just what I want. But for my Unit test I don't want that.
我的断言在我完成模拟的 LookUpIdAsync 之前被调用。在我的正常代码中,这正是我想要的。但是对于我的单元测试,我不想要那样。
I am converting to Async/Await from using BackgroundWorker. With background worker this was functioning correctly because I could wait for the BackgroundWorker to finish.
我正在从使用 BackgroundWorker 转换为 Async/Await。使用后台工作者,这可以正常运行,因为我可以等待 BackgroundWorker 完成。
But there does not seem to be a way to wait for a async void method...
但是似乎没有办法等待 async void 方法......
How can I unit test this method?
如何对这种方法进行单元测试?
采纳答案by Vaccano
I figured out a way to do it for unit testing:
我想出了一种方法来进行单元测试:
[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();
}
The key here is that because I am unit testing I can substitute in the task I want to have my async call (inside my async void) to return. I then just make sure the task has completed before I move on.
这里的关键是,因为我正在进行单元测试,所以我可以替换我想要让我的异步调用(在我的 async void 内)返回的任务。然后,在继续之前,我只需确保任务已完成。
回答by Reed Copsey
An async void
method is essentially a "fire and forget" method. There is no means of getting back a completion event (without an external event, etc).
一种async void
方法本质上是一种“发射后不管”的方法。没有办法恢复完成事件(没有外部事件等)。
If you need to unit test this, I would recommend making it an async Task
method instead. You can then call Wait()
on the results, which will notify you when the method completes.
如果您需要对此进行单元测试,我建议您将其设为一种async Task
方法。然后您可以调用Wait()
结果,该结果会在方法完成时通知您。
However, this test method as written would still not work, as you're not actually testing DoStuff
directly, but rather testing a DelegateCommand
which wraps it. You would need to test this method directly.
但是,这种编写的测试方法仍然不起作用,因为您实际上不是DoStuff
直接测试,而是测试DelegateCommand
包装它的 a 。您需要直接测试此方法。
回答by Stephen Cleary
You should avoid async void
. Only use async void
for event handlers. DelegateCommand
is (logically) an event handler, so you can do it like this:
你应该避免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);
}
and unit test it as:
并将其单元测试为:
[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();
}
My recommended answer is above. But if you really want to test an async void
method, you can do so with my AsyncEx library:
我推荐的答案在上面。但是如果你真的想测试一个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();
}
But this solution changes the SynchronizationContext
for your view model during its lifetime.
但是此解决方案SynchronizationContext
在其生命周期内更改了您的视图模型。
回答by BalintN
You can use an AutoResetEvent to halt the test method until the async call completes:
您可以使用 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);
}
回答by Velimir
The provided answer tests the command and not the async method. As mentioned above you'll need another test to test that async method as well.
提供的答案测试命令而不是异步方法。如上所述,您还需要另一个测试来测试该异步方法。
After spending some time with a similar problem i found an easy wait to test an async method in a unit test by just calling in synchronously:
在遇到类似问题花了一些时间后,我发现只需同步调用即可轻松等待在单元测试中测试异步方法:
protected static void CallSync(Action target)
{
var task = new Task(target);
task.RunSynchronously();
}
and the usage:
和用法:
CallSync(() => myClass.MyAsyncMethod());
The test waits on this line and continues after the result is ready so we can assert immediately afterwards.
测试在这条线上等待并在结果准备好后继续,所以我们可以在之后立即断言。
回答by Alex Planchon
The only way I know is to turn your async void
method to async Task
method
我知道的唯一方法就是把你的async void
方法变成async Task
方法
回答by Billy Jake O'Connor
Change your method to return a Task and you can use Task.Result
更改您的方法以返回任务,您可以使用 Task.Result
bool res = configuration.InitializeAsync(appConfig).Result;
Assert.IsTrue(res);
回答by datchung
I had a similar issue. In my case, the solution was to use Task.FromResult
in the moq setup for .Returns(...)
like so:
我有一个类似的问题。就我而言,解决方案是Task.FromResult
在 moq 设置中使用,.Returns(...)
如下所示:
orderService.LookUpIdAsync(Arg.Any<long>())
.Returns(Task.FromResult(null));
Alternatively, Moq also has a ReturnsAysnc(...)
method.
或者,Moq 也有一个ReturnsAysnc(...)
方法。