单元测试异常的建议

时间:2010-12-20 20:07:46

标签: php exception phpunit

考虑一个可能会抛出一些带有描述性文本的异常的方法:

if ($someCondition) {
    throw new \Whatever\Exception('dilithium exhausted');
}

该方法的其他地方是另一个可能抛出相同异常的块,但文本不同:

if ($anotherCondition) {
    throw new \Whatever\Exception('differentialator exploded');
}

在为此类编写单元测试时,您会创建失败案例,以便您可以验证是否正确抛出了这两个异常。在这些失败案例中,您更愿意:

A)在测试方法的docblock中使用@exceptionExpected来捕获泛型\ Whatever \ Exception类,然后忽略getMessage()文本,假设你得到了正确的文本? (似乎是个坏主意。)

或:

B)使用try / catch然后断言捕获的异常的getMessage()文本等于你期望的确切描述性字符串? (更有弹性,但这意味着每当您更改错误措辞时都会更改测试。)

或:

C)为每个错误情况创建一个单独的异常(例如,\ Whatever \ DilithiumException和\ Whatever \ DifferentialatorException),然后对每个错误使用@exceptionExpected。

我目前正在使用B但是倾向于C.我很好奇其他人在同一场景中做了些什么。您是否有任何指导方针可以帮助您确定“错误在什么时候应该使用自己的异常类而不是更通用的共享类?”

2 个答案:

答案 0 :(得分:2)

以上所有。

A很棒,我尽可能多地使用,因为它最简单。当A不起作用时,还有另一种情况:

/**
 * @exceptionExpected FooException
 */
test() {
  // code that could throw FooException
  ...
  // purpose of the test that throws of FooException
}

在这种情况下,测试可能会在它失败时通过,因为它甚至没有达到我测试的水平。解决这个问题的一个好方法是使用$ this-> setExpectedException()

当您实际使用异常中的信息时,B非常棒。我宁愿使用代码,而不是使用异常消息的文本。我有一个表单验证异常,它将数据中遇到的所有问题打包成一个例外。通过扩展异常类,可以很容易地将大量信息从内部错误状态传输到外部处理代码。

C完成与B相同的操作,但允许通过依赖更多类来简化代码。这两者之间的区别是微妙的,我倾向于依靠设计美学来做出决定。

TL; DR:使用异常代码而不是消息,并设计用例而不是单元测试。

答案 1 :(得分:2)

当您需要此级别的详细信息时,PHPUnit还会提供@expectedExceptionCode@expectedExceptionMessage警告:后者需要前者。

顺便说一句,我也倾向于A.如果我需要在异常中表达更多含义,我更喜欢创建一个新的异常类。我觉得这个消息太不稳定,不值得在大多数应用程序中进行测试。