Sohlae
Sohlae

Reputation: 788

How to Verify if a Method Is Invoked for a Class That Throws an Exception

I have the following test method. What I want to achieve in this test is to verify that the Error method was invoked by method SendSMSAsync() when it received an exception.

[TestMethod]
public async Task SendSMSAsync_PostAsyncRequestResultedInAnException_LogExceptionAsAnError
{
    _client.Setup(clnt => clnt.PostAsync("uri", It.IsAny<HttpContent>()))
        .ThrowsAsync(new Exception("Exception!"));
    var service = new SMSService();
    
    _ = service.SendSMSAsync("mobile_number");

    _logger.Verify(lgr => lgr.Error(exception => exception.Message.Contains("Exception!")))
}

This is the implementation of the service.

public async Task<bool> SendSMSAsync(string mobileNumber)
{
    try
    {
        ...

        // Set up PostAsync to throw an exception.
        using (var response = _client.PostAsync("uri", content))
        {
            ...
        }
    }

    catch(Exception exception)
    {
        _logger.Error(exception);
        throw new Exception("An error has occurred while sending an SMS.");
    }
}

If I run this, my test fails saying that the Test method threw an exception.. Is there a way for me to verify if a method has been invoked inside the catch statement?

Upvotes: 0

Views: 142

Answers (1)

David
David

Reputation: 219057

You could catch the exception as part of the test:

try
{
    _ = service.SendSMSAsync("mobile_number");
}
catch
{
    _logger.Verify(lgr => lgr.Error(exception => exception.Message.Contains("Exception!")));
}

To protect against false positives you might also return from the catch and intentionally fail the test after the catch. Something like:

try
{
    _ = service.SendSMSAsync("mobile_number");
}
catch
{
    _logger.Verify(lgr => lgr.Error(exception => exception.Message.Contains("Exception!")));
    return;
}
throw new Exception("Test failed!");

The idea here is so the test doesn't "pass" if the method doesn't throw an exception in the first place.

Note that there may be tooling within the test framework to more gracefully fail a test, I don't recall off-hand. But the idea is generally the same.


Side note: Shouldn't there be an await on that method call?

Upvotes: 3

Related Questions