【问题标题】:How do you test for the specific reason an exception was thrown?您如何测试引发异常的具体原因?
【发布时间】:2018-01-20 16:59:03
【问题描述】:

确定导致异常的确切原因并纠正它的正确方法是什么?

考虑下面的代码。我尝试解析 XML 字符串,但有时传入的 XML 不是顶级的,这意味着它需要被根元素包围。

如果发生这种情况,解析器会抛出一个XmlException,但它可能会因为很多原因而抛出它。我想了解这一个特定的原因

我这样做,我承认这可能不是很好:

var doc = new XmlDocument();
try
{
    doc.LoadXml(xml);
}
catch(XmlException e)
{
    if(e.Message.Contains("multiple root elements"))
    {
        doc.LoadXml($"<root>{xml}</root>");
    }
    else
    {
        throw e;
    }
}

这感觉就像一个黑客。这样做的正确方法是什么?

【问题讨论】:

  • xml是哪里来的?
  • 看例子here
  • 您可以使用 XmlException.Data 获取更多详细信息
  • @KhalilLazhar,很奇怪,每次我得到 any 异常时,Data 属性都是空字典...我想我从未见过填充 Data 属性的系统异常.. . :(
  • 您重新抛出异常的方式会清除堆栈跟踪。不是理想的情况。字符串解析也不是异常消息。这里有两篇关于正确异常处理的好文章,我链接了很多:blogs.msdn.com/b/ericlippert/archive/2008/09/10/…codeproject.com/Articles/9538/… 很可能,这任何 XML Excpetion 都是外生异常。所以详细解析它们并不是你的工作。

标签: c# xml xmldocument


【解决方案1】:

您可以尝试按照此处所述为XmlException.HResult 进行切换:

https://msdn.microsoft.com/en-us/library/system.xml.xmlexception(v=vs.110).aspx

我唯一不确定的是,它是否指向特定的异常类型(如 XmlException)或特定的异常“消息”。

如果这没有帮助,我认为您除了检查消息别无选择。

编辑:另外,正如上面所指出的,你应该throw; 而不是throw e;,因为第二个清除了StackTrace。 ReSharper 也对此提出警告。

【讨论】:

【解决方案2】:

C# 有一个新特性,允许您在 catch 子句中使用 when 关键字过滤异常:

try
{

} 
catch (XmlException ex) when ( ex.Message.Contains(...) )
{
   //handle
}

您可以使用多个字段来识别异常类型,例如 InnerExceptionStackTraceData。正如@David Hruška 建议的那样,HResult 属性也是检查以识别异常类型的好地方

Message 不是用于检查的最佳属性,因为它通常针对内置类型进行本地化,因此可能与其他区域性设置看起来不同。

【讨论】:

  • 我想重申一下,字符串解析消息不是一个好主意。查看是否有任何字段或包含的异常可以为您提供更多详细信息。
  • 我不确定它是否能解决问题。您无法识别特定异常,因为这是手动检查消息。但是 +1 展示了这个新功能,我完全忘记了这个:)
  • @MartinZikmund,我也会添加HResult,我们有时会检查此属性。
  • 是的,我知道,这只是一个替代方案,但它让我想起了这个很酷的功能,以提醒我自己和 OP :-)。但是@David Hruška,您的解决方案更可靠
  • 消息也可能在其他框架版本中发生变化,这将对您的代码造成重大变化。永远不要那样做。
【解决方案3】:

有多种方法可以实现您想要做的事情。 不同的测试框架也有自己的工具来帮助解决这个问题。 例如,如果您使用 MSVS 测试框架,最简单的选择是只检查异常类型。在这种情况下,您只需用“ExceptedExceptionAttribute”标记测试方法并指定要从该方法抛出的预期异常类型,如下所示:

[TestMethod]
[ExpectedException(typeof(ArgumentException))]
public void Action_ThrowsException()
{
    // test logic here
}

还有另一个可用的构造函数,它允许将确切的异常消息指定为第二个参数,该参数很少使用。您可以在 MSDN 中找到 ExpectedExceptionAttribute 的文档。

另一个选项,你有更多的控制,将是其他答案中已经建议的,可以封装在一个帮助方法中,如下所示:

private static T Throws<T>(Action action) where T : Exception
{
    try
    {
        action();
        Assert.Fail();
    }
    catch (T ex)
    {
        // This exception was expected.
        return ex;
    }
}

使用这个辅助方法,您现在可以拥有如下的测试方法:

[TestMethod]
public void Action_ThrowsException()
{
    // test logic here
    ArgumentException aex = Throws<ArgumentException>(()=>{
        // Test logic here
    });

    Assert.AreEqual("Expected error message", aex.Message);
}

如您所见,此选项为您提供了更高的灵活性,因为您现在可以明确地验证异常的其他方面。

仅供参考:第二种解决方案是 xUnit 框架的一部分。

【讨论】:

  • 这实际上并不是单元测试的一部分。这是运行时代码。我的情况是,有时 XML 会有一个根元素,有时没有,在尝试解析它之前我无法知道。
  • 感谢您提供更多信息。在这种情况下,您可以选择以下选项之一: 1. 使用 if 语句为这两种情况分支您的逻辑 2. 尝试解析最预期的方式,如果失败,请尝试其他逻辑。 “if”选项(选项#1)更可取,因为它会更快并且可能需要更少的资源。这实际上归结为识别 XML sn-p 是否具有根元素,是吗?如果是这样,我将能够分享一些代码来做到这一点。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2013-07-23
  • 1970-01-01
  • 2021-07-19
  • 1970-01-01
  • 2015-11-28
相关资源
最近更新 更多