Twitter LinkedIn Github

JetBrains

Testing for exceptions in unit tests can be tricky. Most frameworks use the ExpectedException attribute to denote that the test will pass when a specific exception is raised. As an example, let's look at the following test:

 

   1: [TestMethod]
   2: [ExpectedException(typeof(AuthenticationException))]
   3: public void Authenticate_With_Invalid_Credentials_Throws_AuthenticationException{
   4:  
   5:     AuthenticationServices services = new AuthenticationServices();
   6:     
   7:     services.Authenticate("user", "wrong");
   8:     
   9: }

In many cases this works. If you are throwing a custom exception, then the chances of the same exception occurring in your code in the wrong place are minimal. However, things change when you are testing for system exceptions. Imagine in the previous example if we were to throw a SecurityException instead of AuthenticationException. The .NET framework could throw a security exception itself due to some specific reason. As far as the test is concerned, it passes because it doesn't care who or where the exception is thrown. It just cares that it's happened.

An alternative approach to this would be to wrap the specific call in a try..catch block and not use the ExpectedException attribute.

The guys that designed xUnit understood the shortcomings of testing exceptions and took a much cleaner approach. In xUnit, the previous test would be written like so:

   1: [Fact]
   2: public void Authenticate_With_Invalid_Credentials_Throws_AuthenticationException()
   3: {
   4:     AuthenticationServices services = new AuthenticationServices();
   5:  
   6:     Exception ex = Assert.Throws<AuthenticationException>(() => services.Authenticate("user", "wrong"));
   7:     
   8:     Assert.Equal("Authentication Failed", ex.Message);
   9: }

As you can see, there is no ExpectedException on the test (called a Fact in xUnit). Instead, the Assert.Throws construct is used. This is a generic method that takes a type parameter the type of exception we want to check for. As parameter we pass a delegate or lambda expression with the actual call that will throw the exception.

The obvious advantage to this is that if any other part of our system were to throw the same type of exception, our test would fails, as it should. So instead of creating a custom exception we were to use SecurityException and on creation of AuthenticationServices the framework would throw a security exception, our test would fail.

Another advantage of Assert.Throws is that it allows you to examine the returned exception object, so you can run further assertions on it (like that on line 8).

 

Note: While I was doing my Unit Testing session at Devreach this week, someone asked me how to test for two exceptions within the same call in Assert.Throws. You don't. Each test should check for only one exception. Remember, a unit test only tests one thing, one situation. If your code is throwing two different exceptions, it's can't be doing it under the same conditions.