When you’re writing unit tests for a method, it’s a good idea to test various failure conditions (“sad paths”) in addition to testing the expected, everything is working condition (“happy path”). In particular, if you have a method that may throw exceptions, especially if they’re custom domain exceptions, you should be sure you’re testing this behavior. Another common source of exceptions is guard clauses, which are used to keep your method clean while ensuring its inputs adhere to the method’s expectations.
Unlike NUnit, which mainly uses attributes for expected exceptions, and MSTest, which has little built-in support at all, xUnit provides an Assert.Throws<T> method that is used to test for expected exceptions (NUnit 3 also has a similar method). A simple example looks like this:
|
|
In addition to this simple usage, the method also returns the captured exception, so you can make additional assertions about the message or other properties of the exception:
|
|
Arrange, Act, Assert and Exceptions
Many tests use the Arrange, Act, Assert, or AAA testing pattern. Using this approach, tests generally have this form:
|
|
Note: Tests should be named to that when they fail, the combination of their class name and method name clearly indicates the expectation that was not met. Read more about unit test naming conventions.
Let’s fill in the details from the Customer.UpdateName() test example, using the AAA template:
|
|
Do you see the problem? The Assert.Throws line is both the Act step as well as one of the assertions. This makes the line both overly long and makes the test harder to read. We can fix this by using an Action to represent the operation that is expected to throw an exception.
Using Actions with Assert.Throws
To help follow the AAA test pattern, represent the operation that is expected to throw an exception as a variable by using an Action:
|
|
Now in this example, the action that is the heart of the test stands alone within the Act section of the test, and the Assert.Throws line is much shorter and easier to follow.
FluentAssertions
If you’re using FluentAssertions instead of the built-in assertions of your testing library, you would do this:
|
|
Thanks to @volkmarrigo for this suggestion!
Local Functions
Another options I learned recently from @snowfrogdev is to use a local function instead of just an Action:
|
|
I’ve been using the Action approach recently, since my friend Shady Nagy (you should follow him on Twitter) pointed it out to me on a project we were working on together. This pattern is really simple to implement but I’ve found it makes tests for exceptions much cleaner.