问题
I have the following method in an interface..
Task<SearchResult<T>> SearchAsync(TU searchOptions);
works great.
Now i'm trying to make a unit test to test when something goes wrong - and the code throws an exception.
In this case, I've setup my method to throw an HttpRequestException
. My unit test fails to say that I threw that exception ..
var result = Should.Throw<HttpRequestException>
(async () => await service.SearchAsync(searchOptions));
the error message from the unit test is
Shouldly.ChuckedAWobbly
var result = Should
throw
System.Net.Http.HttpRequestException
but does not
So the assertion framework is saying: You've expected an exception, but none was thrown.
When I step -through- the code, the exception is 100% thrown.
Can anyone see what i've done wrong with my unit test code, please?
回答1:
The problem is that your assertion framework does not understand asynchronous methods. I recommend you raise an issue with them.
In the meantime, you can use the source for Should.Throw to write your own MyShould.ThrowAsync
:
public static async Task<TException> ThrowAsync<TException>(Func<Task> actual)
where TException : Exception
{
try
{
await actual();
}
catch (TException e)
{
return e;
}
catch (Exception e)
{
throw new ChuckedAWobbly(new ShouldlyMessage(typeof(TException), e.GetType()).ToString());
}
throw new ChuckedAWobbly(new ShouldlyMessage(typeof(TException)).ToString());
}
And use it as such:
var result = await MyShould.ThrowAsync<HttpRequestException>
(async () => await service.SearchAsync(searchOptions));
or the slightly simpler and equivalent:
var result = await MyShould.ThrowAsync<HttpRequestException>
(() => service.SearchAsync(searchOptions));
回答2:
Unit testing async code/functionality is pretty hard. I myself am getting into unit testing async and running into the same problems as you do.
I found the following two resources very helpful:
- Best practices in async programming - it delves into the subject of async and the problems with testing it.
- Unit testing async the wrong way and Unit testing async the right way - delves into the subject, shows the problems that you will encounter and how to setup the testing.
回答3:
Test it like this:
var result = Should.Throw<HttpRequestException>
(() => service.SearchAsync(searchOptions).Result);
Or:
var result = Should.Throw<HttpRequestException>
(() => service.SearchAsync(searchOptions).Wait());
Otherwise, your Should.Throw
returns before the async
lambda has completed.
回答4:
The problem is that the passed lambda returns a Task. The thrown exception can only be observed by Should.Throw
if it waits for this task to complete, which apparently it does not. As a work-around, you can .Wait
on the task returned by SearchAsync
yourself.
mstest
(the built-in visual studio testing framework) has support for async tests since Visual Studio 2012. You can basically just change the test method declaration by replacing "void" by "async Task".
[TestMethod]
[ExpectedException(typeof(System.Net.Http.HttpRequestException))]
public async Task SomeTest()
{
await service.SearchAsync(searchOptions);
}
You are probably using a different unit testing framework, but it's not clear which one. Consult its documentation to see if it supports async tests.
NUnit 2.6.3 also seems to support async tests.
edit: so you are using xUnit. This particular issue was fixed for xUnit 2.0. It's currently still alpha though.
回答5:
The exception is thrown on a different thread to that on which your unit test runs. The unit test framework can only anticipate exceptions on its own thread.
I would suggest you test the exception on a synchronous version of the service.
来源:https://stackoverflow.com/questions/22402701/how-can-i-unit-test-this-async-method-which-correctly-throws-an-exception