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

Posted

技术标签:

【中文标题】如何使用 Visual Studio 测试中资源文件中的特定异常消息测试预期异常?【英文标题】:How can I test for an expected exception with a specific exception message from a resource file in Visual Studio Test? 【发布时间】:2010-09-11 22:05:22 【问题描述】:

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()

编译器会报如下错误:

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

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


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

"创建并抛出自定义异常 如果您有错误情况 可以以编程方式处理 不同于任何其他现有的方式 例外。否则,抛出其中一个 现有异常。”Source

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

【问题讨论】:

message 参数没有根据抛出异常的消息进行检查。 【参考方案1】:

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

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

这还有一个好处,它可以验证异常是在您期望它被抛出的那一行而不是在您的测试方法中的任何地方抛出的。

【讨论】:

【参考方案2】:

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

【讨论】:

【参考方案3】:

只是一个意见,但我会说错误文本:

是测试的一部分,在这种情况下,从资源中获取它是“错误的”(否则您最终可能会得到一致损坏的资源),因此只需在更改资源时更新测试(或测试失败) ) 不是测试的一部分,您应该只关心它是否会引发异常。

请注意,第一个选项应该允许您测试多种语言,前提是能够使用语言环境运行。

至于多个异常,我来自 C++ 领域,在那里创建大量异常(到每个“抛出”语句一个!)在大范围内是可以接受的(如果不常见的话),但是 .Net 的元数据系统可能不喜欢这样,因此建议。

【讨论】:

我会实现一个简单的过程来读取资源文件并找到要写入的消息,你的方法是一个很好的去映射和浪费宝贵时间更新测试的方法(我们最想避免) @Mickey:那么,你很幸运:另一个答案就是你要找的!如果您特别想要一个 unit-测试它检索相同的消息,那很好,但我倾向于它检索 right 消息.但如果你的项目不同,那么你很容易得到不同的“正确”答案! 我不跟,使用DataBound单元测试,你可以把蛋糕留着吃。映射数据绑定中的资源并映射测试中的消息文化,反之亦然。没有? @Mickey:是的,你可以,但我原来的帖子指出,你首先不会捕获错误定义的消息:我的立场是,当测试时,您根据显式值检查结果,而不是应该给出相同结果的东西:对于执行2 + 2 的方法,您测试它返回4,而不是2 + 2。如果您关心消息是什么,那么我认为您应该清楚地检查它是否给出了 right 结果,而不仅仅是 same 结果。但是,如果您有理由,请随时以不同的方式做事! 直到现在我才真正理解这一点。在这种情况下,我同意【参考方案4】:

我认为你可以在你的测试代码中做一个显式的 try-catch,而不是依赖 ExpectedException 属性来为你做这件事。然后你可以想出一些帮助方法来读取资源文件并将错误消息与捕获的异常所带来的错误消息进行比较。 (当然,如果没有异常,那么测试用例应该被认为是失败的)

【讨论】:

【参考方案5】:

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

[Fact]
public void TestException()

   Exception ex = Record.Exception(() => myClass.DoSomethingExceptional());
   // Assert whatever you like about the exception here.

【讨论】:

【参考方案6】:

我想知道 NUnit 是否正朝着远离简单的方向发展……但你去吧。

ExpectedException 属性的新增强功能(2.4.3 及更高版本?)允许您更好地控制通过 Handler 方法对预期异常执行的检查。更多详情请参阅页面末尾的official NUnit doc page..。

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

...


public void HandlerMethod( System.Exception ex )

...

注意:这里感觉有些不对劲。为什么您的异常消息要国际化。您是否将异常用于需要处理或通知用户的事情。除非你有一群文化不同的开发人员修复错误......你不应该需要这个。英语或通用接受语言的例外情况就足够了。但万一你必须有这个..它可能:)

【讨论】:

你不认为从抛出的异常中获取错误对话框消息的方法是好的设计吗?我的印象是它很常见,而且它简化了错误路径代码......我知道我不想看到中文对话框! 我使用的是 NUnit 框架 2.6 版,属性 [ExpectedException( Handler="HandlerMethod" )] 无法编译 @mishrsud - 我相信编译器会给你更多的细节。检查输出窗格 - 它在抱怨什么? @Gishu 编译器抱怨“未知符号”,所以它似乎不知道属性的这个特殊签名 @mishrsud- 该示例直接来自 nunit v2.6.2 nunit.org/index.php?p=exception&r=2.6.2 的文档页面 |你能检查一下你是否没有选择错误版本的程序集,是否有必要使用 stmts,清理项目?只是在这里大声思考......【参考方案7】:

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

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

我最初是在我自己的项目中这样做的,以便我可以在我的应用程序抛出的错误消息和我的单元测试中保持一致。即,当时只需在一个地方定义我的异常消息,资源文件似乎是一个明智的地方,因为我已经将它用于各种标签和字符串(并且因为添加参考是有意义的在我的测试代码中对其进行验证,以验证这些相同的标签是否显示在适当的位置)。

我曾考虑(并测试过)使用 try/catch 块来避免 ExpectedException 属性对常量的要求,但如果大规模应用,这似乎会导致相当多的额外代码.

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

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

[Test, 
    ExpectedException(typeof(System.ArgumentException),
    ExpectedException=ProductExceptionMessages.DuplicateProductName)]
public void TestCreateDuplicateProduct()

    _repository.CreateProduct("TestCreateDuplicateProduct");
    _repository.CreateProduct("TestCreateDuplicateProduct");
 

【讨论】:

以上是关于如何使用 Visual Studio 测试中资源文件中的特定异常消息测试预期异常?的主要内容,如果未能解决你的问题,请参考以下文章

Visual Studio 2008 / C#:如何在项目中查找死代码?

Visual Studio 2019在测试资源管理器中显示重复的测试

visual studio 2010 一个解决方案里有多个c++源文件 怎么只执行其中一个?

visualstudio如何打开菜单资源

如何更改解决方案资源管理器的字体大小(在 Visual Studio 中)

visual studio2013负载测试简单问题记录