[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 -
Any comparision on these behaviour and its cause(s) would be great!
It's also in a class by itself in that it returns an Exception, rather than void, if the Assert is successful. See the example below for a few ways to use this. Assert. Throws may be used with a constraint argument, which is applied to the actual exception thrown, or with the Type of exception expected.
So, the moral of the story is: you can write async void unit tests in NUnit 2.6. It also works for delegates passed to Assert. Throws , which can have an async modified.
Assert. Throws returns the exception that's thrown which lets you assert on the exception. var ex = Assert.
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.
I'm not sure when it was added, but the current version of Nunit (3.4.1 at time of writing) includes a ThrowsAsync method
see https://github.com/nunit/docs/wiki/Assert.ThrowsAsync
Example:
[Test] public 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 exception = Assert.ThrowsAsync<HttpResponseException>(() => userController.Get("foo")); Assert.That(exception.Response.StatusCode, Is.EqualTo(HttpStatusCode.NotFound)); }
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With