How can I test for an expected exception with a specific exception message from a resource file in Visual Studio Test?

Alex Duggleby picture Alex Duggleby · Sep 22, 2008 · Viewed 34.9k times · Source

Visual Studio Test can check for expected exceptions using the ExpectedException attribute. You can pass in an exception like this:

[TestMethod]
[ExpectedException(typeof(CriticalException))]
public void GetOrganisation_MultipleOrganisations_ThrowsException()

You can also check for the message contained within the ExpectedException like this:

[TestMethod]
[ExpectedException(typeof(CriticalException), "An error occured")]
public void GetOrganisation_MultipleOrganisations_ThrowsException()

But when testing I18N applications I would use a resource file to get that error message (any may even decide to test the different localizations of the error message if I want to, but Visual Studio will not let me do this:

[TestMethod]
[ExpectedException(typeof(CriticalException), MyRes.MultipleOrganisationsNotAllowed)]
public void GetOrganisation_MultipleOrganisations_ThrowsException()

The compiler will give the following error:

An attribute argument must be a constant expression, typeof expression or array creation expression of an attribute

Does anybody know how to test for an exception that has a message from a resource file?


One option I have considered is using custom exception classes, but based on often heard advice such as:

"Do create and throw custom exceptions if you have an error condition that can be programmatically handled in a different way than any other existing exception. Otherwise, throw one of the existing exceptions." Source

I'm not expecting to handle the exceptions differently in normal flow (it's a critical exception, so I'm going into panic mode anyway) and I don't think creating an exception for each test case is the right thing to do. Any opinions?

Answer

Daniel Plaisted picture Daniel Plaisted · Sep 22, 2008

I would recommend using a helper method instead of an attribute. Something like this:

public static class ExceptionAssert
{
  public static T Throws<T>(Action action) where T : Exception
  {
    try
    {
      action();
    }
    catch (T ex)
    {
      return ex;
    }
    Assert.Fail("Exception of type {0} should be thrown.", typeof(T));

    //  The compiler doesn't know that Assert.Fail
    //  will always throw an exception
    return null;
  }
}

Then you can write your test something like this:

[TestMethod]
public void GetOrganisation_MultipleOrganisations_ThrowsException()
{
  OrganizationList organizations = new Organizations();
  organizations.Add(new Organization());
  organizations.Add(new Organization());

  var ex = ExceptionAssert.Throws<CriticalException>(
              () => organizations.GetOrganization());
  Assert.AreEqual(MyRes.MultipleOrganisationsNotAllowed, ex.Message);
}

This also has the benefit that it verifies that the exception is thrown on the line you were expecting it to be thrown instead of anywhere in your test method.