'How do I obtain 100% coverage when an exception is thrown in a unit test?

In C# you can catch an exception in the default test suite like this:

[TestMethod]
[ExpectedException(typeof (ArgumentNullException))]
public void TestNullComposite()
{
    IApi api = new Api();
    api.GetDataUsingDataContract(null); // this method throws ArgumentNullException
}

But when you analyze the code coverage, it says that you only get 66.67% coverage because the last curly brace was not covered.

How would I go about achieving 100% coverage on this unit test?



Solution 1:[1]

Within NUnit you have a method Assert.Throws<Exception>(), which checks if the desired exception was thrown. It also returns that exception as return value, so that you could have further assertations if you like:

[Test]
public void Api_GetDataUsingDataContractWithNullParameter_ThrowsArgumentNullException()
{
    var api = new Api();
    var exception = Assert.Throws<ArgumentNullException>(() => api.GetDataUsingDataContract(null));
    Assert.That(exception.Message, Is.Not.Null.Or.Empty);
    Assert.That(exception.Message, Is.StringContaining("source"));
}

Due to the fact, that the method does not throw by itself, your coverage would be 100%.

Solution 2:[2]

Usually when people are measuring code coverage, they are looking at the code covered by the tests and not the tests themselves.

As this case shows, it doesn't really make sense to require 100% coverage on test units.

The test is supposed to throw. That is what you are testing.

If you really want the entire method to be executed I guess you could test whether the exception was thrown manually. Something like this (haven't tested it, but I don't see why it shouldn't work):

[TestMethod]
public void TestNullComposite()
{
    IApi api = new Api();
    bool didThrow = false;
    try
    {
        api.GetDataUsingDataContract(null); // this method throws ArgumentNullException
    }
    catch(ArgumentNullException) 
    {
        didThrow = true;
    }
    Assert.IsTrue(didThrow);
}

But it seems like extra work for no good reason. I would suggest you re-evaluate your testing practices.

Solution 3:[3]

What MAV is saying is true. Additionally, there is a way to exclude the test class from being analyzed from Code Coverage.

Just adorn your [TestClass] with the Attribute [ExcludeFromCodeCoverage]!

This way it is at least theoretically possible to reach 100% CC.

Solution 4:[4]

Yes You Can But First You have to understand see assert throw will fully cover when

  1. The expected exception match with the exception thrown by your function e.g

    assertThrows(NullPointerException.class,() -> userProfileService.getUserDetailById(userProfile.getAssociateId()));

  2. When the expected and actual exception are not match or no any exception are thrown by assertthrow. Consider the above scenario example suppose called function will throw Other exception than NullPointerException then assertthrow will fail.

Now you have to think to fail and pass at the same time for assertthrow.

So a solution that I find is

userProfile = new UserProfile("101", "Amit Kumar Gupta", "[email protected]", "Software Intern", "No",
                "999999999");
        userProfile2 = new UserProfile("102", "Satyam Sharma", "[email protected]", "Software Engineer", "Yes",
                "8769356421");
        Mockito.when(userProfileDAO.findById(Mockito.anyString())).thenReturn(Optional.ofNullable(userProfile))
                .thenReturn(Optional.ofNullable(userProfile2));
List<Class<? extends Exception>> exceptionClassList = new ArrayList<Class<? extends Exception>>();
        exceptionClassList.add(NullPointerException.class);
        exceptionClassList.add(IllegalArgumentException.class);
        for (int i = 0; i < 2; i++) {
            try {
                assertThrows(exceptionClassList.get(i),
                        () -> userProfileService.getUserDetailById(userProfile.getAssociateId()));
            } catch (AssertionError e) {
                assertNotNull(e.getMessage());
            }
        }

In this case Mockito will return two values and both will be used by function call inside the assertThrow first time assertThrow will match the expected exception i.e. NullPointerException.class

Second time assertThrow will Fail because the lambda function will not throw any exception but assertThrow is expecting a IllegalArgumentException.

It generates the AssertionError So that will caught by catch block.

Now You have 100% coverage.

Sources

This article follows the attribution requirements of Stack Overflow and is licensed under CC BY-SA 3.0.

Source: Stack Overflow

Solution Source
Solution 1 Oliver
Solution 2 MAV
Solution 3
Solution 4 BeBlack