如何使用Visual Studio Test中的资源文件中的特定异常消息测试预期的异常?

时间:2008-09-22 06:08:16

标签: visual-studio unit-testing exception internationalization

Visual Studio Test可以使用ExpectedException属性检查预期的异常。你可以传递这样的例外:

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

您还可以检查ExpectedException中包含的消息,如下所示:

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

但是在测试I18N应用程序时,我会使用资源文件来获取该错误消息(如果我愿意,任何人甚至可能决定测试错误消息的不同本地化,但Visual Studio不允许我这样做:

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

编译器将给出以下错误:

  

属性参数必须是a   常量表达式,表达式   或者数组创建表达式   属性

有人知道如何测试来自资源文件的消息的异常吗?


我考虑过的一个选项是使用自定义异常类,但基于经常听到的建议,例如:

  

“创建并抛出自定义异常   如果你有错误的条件   可以通过编程方式处理   与现有的任何其他方式不同   例外。否则,抛出其中一个   现有的例外情况。“Source

我不期望在正常流程中以不同的方式处理异常(这是一个关键的例外,所以我无论如何都会进入恐慌模式)并且我不认为为每个测试用例创建一个例外是正确的做。有什么意见吗?

7 个答案:

答案 0 :(得分:63)

我建议使用辅助方法而不是属性。像这样:

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;
  }
}

然后你可以写下你的测试:

[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);
}

这样做的好处是,它可以验证在您希望抛出的行上抛出异常,而不是在测试方法中的任何位置抛出异常。

答案 1 :(得分:14)

ExpectedException Message参数与异常消息不匹配。相反,如果预期的异常实际上没有发生,则这是在测试结果中打印的消息。

答案 2 :(得分:7)

只是意见,但我会说错误文字:

  • 是测试的一部分,在这种情况下,从资源中获取它将是“错误的”(否则您最终会得到一个受到严重破坏的资源),所以只需在更改资源时更新测试(或测试失败) )
  • 不是测试的一部分,你应该只关心它会抛出异常。

请注意,第一个选项应该允许您测试多种语言,因为能够使用区域设置运行。

至于多个异常,我来自C ++的土地,在大型heirachies中创建加载和异常加载(到每个'throw'语句一个点!)是可以接受的(如果不常见),但.Net的元数据系统可能不喜欢这样,因此建议。

答案 3 :(得分:4)

我认为您可以在测试代码中执行显式的try-catch,而不是依赖于ExpectedException属性来为您执行此操作。然后,您可以提供一些帮助方法来读取资源文件,并将错误消息与捕获的异常附带的错误消息进行比较。 (当然,如果没有例外,则应将测试用例视为失败)

答案 4 :(得分:3)

如果切换到使用非常好的xUnit.Net测试库,则可以用以下内容替换[ExpectedException]:

[Fact]
public void TestException()
{
   Exception ex = Record.Exception(() => myClass.DoSomethingExceptional());
   // Assert whatever you like about the exception here.
}

答案 5 :(得分:1)

我想知道NUnit是否正在走远离简单的道路......但是你走了。

ExpectedException属性的新增强功能(2.4.3及更高版本)允许您通过Handler方法对预期的Exception执行检查的更多控制。关于页面末尾的official NUnit doc page ..的更多详细信息。

[ExpectedException( Handler="HandlerMethod" )]
public void TestMethod()
{
...
}

public void HandlerMethod( System.Exception ex )
{
...
}

注意:这里感觉不对。为什么您的例外消息国际化..您是否对需要处理或通知用户的事物使用例外。除非你有一群文化多样的开发人员修复错误..你不应该需要这个。英语例外或普通语言就足够了。但是如果你必须拥有它......它可能:)

答案 6 :(得分:0)

我在尝试自己解决类似问题时遇到了这个问题。 (我将详细说明我在下面解决的解决方案。)

我必须同意Gishu关于将异常消息国际化为代码气味的评论。

我最初在自己的项目中完成了这项工作,这样我就可以在我的应用程序和单元测试中抛出的错误消息之间保持一致。也就是说,只需要在一个地方和当时定义我的异常消息,资源文件似乎是一个合理的地方,因为我已经将它用于各种标签和字符串(并且因为添加引用是有意义的在我的测试代码中验证那些相同的标签是否显示在适当的位置。)

有一次我考虑(并测试过)使用try / catch块来避免ExpectedException属性要求一个常量,但是如果大规模应用它会导致相当多的额外代码

最后,我解决的解决方案是在我的资源库中创建一个静态类,并将异常消息存储在其中。这样就不需要对它们进行国际化(我同意这没有意义),并且只要资源字符串可以访问它们就可以访问它们,因为它们位于同一名称空间中。 (这符合我不希望将异常文本验证为复杂过程的愿望。)

我的测试代码然后简单地归结为(原谅重整......):

[Test, 
    ExpectedException(typeof(System.ArgumentException),
    ExpectedException=ProductExceptionMessages.DuplicateProductName)]
public void TestCreateDuplicateProduct()
{
    _repository.CreateProduct("TestCreateDuplicateProduct");
    _repository.CreateProduct("TestCreateDuplicateProduct");
}