Nunit async test exception assertion
async-await, c#, nunit
Solution
You're seeing problems due to `async void`.
In particular:
`async () => await userController.Get("foo")` is converted into `TestDelegate`, which returns `void`, so your lambda expression is treated as `async void`. So the test runner will begin executing the lambda but not wait for it to complete. The lambda returns before `Get` completes (because it's `async`), and the test runner sees that it returned without an exception.
`Wait` wraps any exceptions in an `AggregateException`.
Again, the `async` lambda is being treated as `async void`, so the test runner is not waiting for its completion.
I recommend you make this `async Task` rather than `async void`, but in this case the test runner does wait for completion, and thus sees the exception.
According to this bug report, there is a fix for this coming in the next build of NUnit. In the meantime, you can build your own `ThrowsAsync` method; an example for xUnit is here.
Problem
[Edit (May 2020)] - This issue has been reportedly addressed in newer releases of NUnit. Please see Nunit.ThrowsAsync. (Ref this answer, thanks @James-Ross) I have a controller `UserController` with this action ``` // GET /blah public Task<User> Get(string domainUserName) { if (string.IsNullOrEmpty(domainUserName)) { throw new ArgumentException("No username specified."); } return Task.Factory.StartNew( () => { var user = userRepository.GetByUserName(domainUserName); if (user != null) { return user; } throw new HttpResponseException(Request.CreateErrorResponse(HttpStatusCode.NotFound, string.Format("{0} - username does not exist", domainUserName))); }); } ``` I am trying to write a test for the case where I throw a 404 exception. Here is what I have tried, with the output - 1) ``` [Test] public void someTest() { var mockUserRepository = new Mock<IUserRepository>(); mockUserRepository.Setup(x => x.GetByUserName(It.IsAny<string>())).Returns(default(User)); var userController = new UserController(mockUserRepository.Object) { Request = new HttpRequestMessage() }; Assert.That(async () => await userController.Get("foo"), Throws.InstanceOf<HttpResponseException>()); } ``` Result Test Failed ``` Expected: instance of <System.Web.Http.HttpResponseException> But was: no exception thrown ``` [Test] public void someTest() { var mockUserRepository = new Mock(); mockUserRepository.Setup(x => x.GetByUserName(It.IsAny())).Returns(default(User)); var userController = new UserController(mockUserRepository.Object) { Request = new HttpRequestMessage() }; ``` var httpResponseException = Assert.Throws<HttpResponseException>(() => userController.Get("foo").Wait()); Assert.That(httpResponseException.Response.StatusCode, Is.EqualTo(HttpStatusCode.NotFound)); ``` } Result Test failed ``` Expected: <System.Web.Http.HttpResponseException> But was: <System.AggregateException> (One or more errors occurred.) ``` ``` [Test] public void someTest() { var mockUserRepository = new Mock<IUserRepository>(); mockUserRepository.Setup(x => x.GetByUserName(It.IsAny<string>())).Returns(default(User)); var userController = new UserController(mockUserRepository.Object) { Request = new HttpRequestMessage() }; var httpResponseException = Assert.Throws<HttpResponseException>(async () => await userController.Get("foo")); Assert.That(httpResponseException.Response.StatusCode, Is.EqualTo(HttpStatusCode.NotFound)); } ``` Result Test Failed ``` Expected: <System.Web.Http.HttpResponseException> But was: null ``` ``` [Test] [ExpectedException(typeof(HttpResponseException))] public async void ShouldThrow404WhenNotFound() { var mockUserRepository = new Mock<IUserRepository>(); mockUserRepository.Setup(x => x.GetByUserName(It.IsAny<string>())).Returns(default(User)); var userController = new UserController(mockUserRepository.Object) { Request = new HttpRequestMessage() }; var task = await userController.Get("foo"); } ``` Result Test passes Questions - - Why would Assert.Throws not handle HttpResponseException, when ExpectedException does? - I don't want to just test that exception is thrown. I want to assert on the Status Code of the response. What's the way to do this? Any comparision on these behaviour and its cause(s) would be great!