In MSTest, How can I verify exact error message using [ExpectedException(typeof(ApplicationException))]

Viewed 12369

Using MSTest how can I verify the exact error message coming from a test method? I know [ExpectedException(typeof(ApplicationException), error msg)] doesn't compare the error message coming from my test method, though in other unit test framework it is doing.

One way to solve this problem is to write my unit test using some try catch block, but again I need to write 4 lines more.

Is there any smartest way to check the error message.

Cheers, Pritam

11 Answers

Use this little helper class:

public static class ExceptionAssert
{
    public static void Throws<TException>(Action action, string message)
        where TException : Exception
    {
        try
        {
            action();

            Assert.Fail("Exception of type {0} expected; got none exception", typeof(TException).Name);
        }
        catch (TException ex)
        {
            Assert.AreEqual(message, ex.Message);
        }
        catch (Exception ex)
        {
            Assert.Fail("Exception of type {0} expected; got exception of type {1}", typeof(TException).Name, ex.GetType().Name);               
        }
    }
}

Usage:

Foo foo = new Foo();
foo.Property = 42;

ExceptionAssert.Throws<InvalidOperationException>(() => foo.DoSomethingCritical(), "You cannot do anything when Property is 42.");

The advantage of explicit catching exceptions is that teh test does not succeed when another member (e.g. during the initialization) throws the exception.

MSTest v2 supports Assert.Throws and Assert.ThrowsAsync which returns the captured exception.

Here is an article on how to upgrade to MSTest v2: https://blogs.msdn.microsoft.com/devops/2017/09/01/upgrade-to-mstest-v2/

Here is example usage:

var myObject = new MyObject();
var ex = Assert.Throws<ArgumentNullException>(() => myObject.Do(null));
StringAssert.Contains(ex.Message, "Parameter name: myArg");

In MSTest there's no built-in way of doing it. This is about as 'elegant' as it gets:

[TestMethod]
public void Test8()
{
    var t = new Thrower();
    try
    {
        t.DoStuffThatThrows();
        Assert.Fail("Exception expected.");
    }
    catch (InvalidOperationException e)
    {
        Assert.AreEqual("Boo hiss!", e.Message);
    }
}

However, you could consider porting xUnit.NET's Assert.Throws API to a custom library - that's what we did.

You could also go to Microsoft Connect an vote on this suggestion.

With MSTest, you can't do this.

You already know the solution to this problem: assert the message of an exception in a catch block.

MbUnit can also do this:

[Test]
[Row(ExpectedExceptionMessage="my message")]
void TestBlah(...

This code does it in the async/await scenario:

Code Reference

public async static Task AssertThrowsAsync<T>(Task task, string expectedMessage) where T : Exception
{
    try
    {
        await task;
    }
    catch (Exception ex)
    {
        if (ex is T)
        {
            Assert.AreEqual(expectedMessage, ex.Message);
            return;
        }

        Assert.Fail($"Expection exception type: {typeof(T)} Actual type: {ex.GetType()}");
    }

    Assert.Fail($"No exception thrown");
}

Example Usage: Code Reference

    [TestMethod]
    public async Task TestBadRequestThrowsHttpStatusCodeException()
    {
        var mockHttp = new MockHttpMessageHandler();

        const HttpStatusCode statusCode = HttpStatusCode.BadRequest;

        mockHttp.When("https://restcountries.eu/rest/v2/")
                .Respond(statusCode, "application/json", JsonConvert.SerializeObject(new { Message = "Test", ErrorCode = 100 }));

        var httpClient = mockHttp.ToHttpClient();

        var factory = new SingletonHttpClientFactory(httpClient);

        var baseUri = new Uri("https://restcountries.eu/rest/v2/");
        var client = new Client(new NewtonsoftSerializationAdapter(), httpClientFactory: factory, baseUri: baseUri, logger: _logger.Object);

        await AssertThrowsAsync<HttpStatusException>(client.GetAsync<List<RestCountry>>(), Messages.GetErrorMessageNonSuccess((int)statusCode, baseUri));
    }

This is the equivalent for synchronous situations

public static void AssertThrows<T>(Action action, string expectedMessage) where T : Exception
{
    try
    {
        action.Invoke();
    }
    catch (Exception ex)
    {
        if (ex is T)
        {
            Assert.AreEqual(expectedMessage, ex.Message);
            return;
        }

        Assert.Fail($"Expection exception type: {typeof(T)} Actual type: {ex.GetType()}");
    }

    Assert.Fail($"No exception thrown");
}

Note: this asserts that the exception inherits from a given type. If you want to check for the specific type you should check for Type equality instead of using the is operator.

Update: Oops.. see that you want this in MSTest. Sorry. Speed read & Misled by your title.

Try this extension project from Callum Hibbert and see if it works.

Old response:

You can do this with NUnit 2.4 and above. See documentation of ExpectedException here

[ExpectedException( typeof( ArgumentException), ExpectedMessage="unspecified", MatchType=MessageMatch.Contains )]
public void TestMethod()
{
...

MatchType can be Exact (default), Contains or Regex.. which pretty much handles the 80% use-case. There is also an advanced exception handler method approach if the verification gets too complex.. never used it personally.. didn't need it yet.

Related