Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How to handle exceptions thrown by Tasks in xUnit .net's Assert.Throws<T>?

Tags:

The following asynchronous xUnit.net test with a lambda marked with the async modifier fails by reporting that no exception was thrown:

    [Theory, AutoWebData]     public async Task SearchWithNullQueryThrows(         SearchService sut,         CancellationToken dummyToken)     {         // Fixture setup         // Exercise system and verify outcome         Assert.Throws<ArgumentNullException>(async () =>             await sut.SearchAsync(null, dummyToken));         // Teardown     } 

To make sure that an ArgumentNullException is actually thrown I explicitly used a try-catch block. It worked, however the resulting code is not clean (compared to the first test):

[Theory, AutoWebData] public async Task SearchWithNullQueryThrows(     SearchService sut,     CancellationToken dummyToken) {     // Fixture setup     var expected = typeof(ArgumentNullException);     Type actual = null;     // Exercise system     try     {         await sut.SearchAsync(null, dummyToken);     }     catch (ArgumentNullException e)     {         actual = e.GetType();     }     // Verify outcome     Assert.Equal(expected, actual);     // Teardown } 

Why the Assert.Throws<T> with the lambda marked with the async modifier fails?

like image 589
Nikos Baxevanis Avatar asked Dec 29 '12 18:12

Nikos Baxevanis


People also ask

How do you assert exception is thrown xUnit?

Assert. Throws<Exception>(() => SomethingThatThrowsAnException()); If the method SomethingThatThrowsAnException() from the above throws an exception the assertion passes, if it does not throw an exception, the assertion will fail and thereby the test fails. It is as simple as that.

Do asserts throw exceptions?

Assert. Throws returns the exception that's thrown which lets you assert on the exception.

How do you test a method using exceptions?

If you need to test for exceptions, there are less frowned upon ways. You can use the try{act/fail}catch{assert} method, which can be useful for frameworks that don't have direct support for exception tests other than ExpectedException .


1 Answers

Update

This has been solved in xUnit 2, with the addition of Assert.ThrowsAsync.


I am suspecting that Assert.Throws is not async-aware. I recommend raising this issue with the xUnit team, suggesting a ThrowsAsync be added.

An async delegate in this case is returning Task or Task<T>, and the ArgumentNullException is not thrown out of the delegate directly; instead, it is placed on the Task (Task.Exception.InnerException). Assert.Throws is expecting the exception to be thrown out of the delegate directly, not placed on a property of the return value.

You can create your own AssertEx.ThrowsAsync as such:

public static async Task ThrowsAsync<TException>(Func<Task> func) {   var expected = typeof(TException);   Type actual = null;   try   {     await func();   }   catch (Exception e)   {     actual = e.GetType();   }   Assert.Equal(expected, actual); } 

which can be used as such:

[Theory, AutoWebData] public async Task SearchWithNullQueryThrows(     SearchService sut,     CancellationToken dummyToken) {     // Fixture setup     // Exercise system and verify outcome     await AssertEx.ThrowsAsync<ArgumentNullException>(async () =>         await sut.SearchAsync(null, dummyToken));     // Teardown } 

I use a similar approach in MSTest.

like image 126
Stephen Cleary Avatar answered Sep 29 '22 15:09

Stephen Cleary