13

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?

2
  • Is the exception by any change caught by something in between? Commented Mar 14, 2014 at 10:49
  • Does this answer your question? Unit testing async method for specific exception Commented Aug 21, 2020 at 9:59

6 Answers 6

14

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)); 
Sign up to request clarification or add additional context in comments.

1 Comment

FYI the task overloads are in another file since v2 (Feb 2014). github.com/shouldly/shouldly/blob/master/src/Shouldly/… Shouldly does block though so the test doesn't need to await the Should.Throw method. I think we will add Should.ThrowAsync which is asynchronous though
4

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 Comments

First line doesn't compile.
@Pure.Krome, it means Should.Throw expects a void lambda, as far as can tell. The second one does the same, without peeking into the result.
Result and Wait both wrap exceptions into an AggregateException, so this answer won't work as-is.
This can also cause a deadlock if there is a synchronization context set here, as you're synchronously blocking on an asynchronous method.
3

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:

Comments

3

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.

1 Comment

The passed lambda actually returns void, and it's very hard to catch exceptions from async void methods. Wait wraps its exceptions in AggregateException, so it doesn't work.
2

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.

3 Comments

So -- are you saying that the async/await isn't working and the exception is not coming through and I can't test for this thing?
But .. there is no sync method.
No, that is normal behaviour. This may give some context on exception handling for async tasks msdn.microsoft.com/en-us/magazine/jj991977.aspx
1

Today Shouldly contains Should.ThrowAsync<T>.

You can use it like this:

await Should.ThrowAsync<HttpRequestException>(() => service.SearchAsync(searchOptions)); 

Comments

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.