단위 테스트를위한 Async Void 메서드 호출 대기
다음과 같은 방법이 있습니다.
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);
}
다음과 같이 단위 테스트를 시도하고 있습니다.
[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();
}
모의 LookUpIdAsync를 완료하기 전에 내 주장이 호출됩니다. 내 정상적인 코드에서 그것은 내가 원하는 것입니다. 그러나 내 단위 테스트에서는 그것을 원하지 않습니다.
BackgroundWorker를 사용하여 Async / Await로 변환 중입니다. 백그라운드 작업자를 사용하면 BackgroundWorker가 완료 될 때까지 기다릴 수 있기 때문에 올바르게 작동했습니다.
그러나 비동기 void 메서드를 기다리는 방법이없는 것 같습니다.
이 방법을 어떻게 단위 테스트 할 수 있습니까?
피해야 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);
}
단위 테스트는 다음과 같습니다.
[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();
}
내 권장 답변은 위입니다. 그러나 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();
}
그러나이 솔루션 SynchronizationContext
은 수명 동안 뷰 모델을 변경합니다 .
async void
방법은 본질적으로 "화재와 잊지"입니다 방법. 완료 이벤트를 되돌릴 수있는 방법은 없습니다 (외부 이벤트 등없이).
단위 테스트가 필요한 경우 async Task
대신 메서드로 만드는 것이 좋습니다 . 그런 다음 Wait()
결과 를 호출 하면 메서드가 완료되면 알려줍니다.
그러나 작성된이 테스트 방법은 실제로 DoStuff
직접 테스트하는 것이 아니라 DelegateCommand
그것을 감싸는 테스트이므로 여전히 작동하지 않습니다 . 이 방법을 직접 테스트해야합니다.
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.
You can use an AutoResetEvent to halt the test method until the async call completes:
[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);
}
The only way I know is to turn your async void
method to async Task
method
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.
Change your method to return a Task and you can use Task.Result
bool res = configuration.InitializeAsync(appConfig).Result;
Assert.IsTrue(res);
I had a similar issue. In my case, the solution was to use Task.FromResult
in the moq setup for .Returns(...)
like so:
orderService.LookUpIdAsync(Arg.Any<long>())
.Returns(Task.FromResult(null));
Alternatively, Moq also has a ReturnsAysnc(...)
method.
참고URL : https://stackoverflow.com/questions/14205590/await-a-async-void-method-call-for-unit-testing
'Programing' 카테고리의 다른 글
Powershell Get-ChildItem 디렉터리의 최신 파일 (0) | 2020.11.27 |
---|---|
Gmail을 통해 간단한 SMTP 명령을 사용하여 이메일을 보내는 방법은 무엇입니까? (0) | 2020.11.27 |
char + char = int? (0) | 2020.11.27 |
#if debug-> #if myOwnConfig? (0) | 2020.11.27 |
queue : work --daemon과 queue : listen의 차이점은 무엇입니까? (0) | 2020.11.27 |