为什么要测试异常流? 就像所有代码一样,测试覆盖率会在代码和应该生成的业务功能之间写一个合同,从而为您提供代码的有效文档 ,以及增加的尽早且经常强调功能的功能。 我不会介绍测试的许多好处,而是只关注异常测试。
有很多方法可以测试从一段代码引发的异常流。 假设您有一个受保护的方法,该方法要求参数不为null。 您将如何测试该状况? 引发异常时,如何防止JUnit报告失败? 该博客涵盖了几种不同的方法使用JUnit的的ExpectedException使用JUnit的@rule功能实现的高潮。
“旧”方式
在不久的将来,测试异常的过程需要大量的样板代码,您可以在其中启动try / catch块,如果代码未产生预期的行为,则报告失败,然后捕获异常以查找异常。具体类型。 这是一个例子:
public class MyObjTest {@Testpublic void getNameWithNullValue() {try {MyObj obj = new MyObj();myObj.setName(null);fail('This should have thrown an exception');} catch (IllegalArgumentException e) {assertThat(e.getMessage().equals('Name must not be null'));}}
}
从这个旧示例中可以看到,测试用例中的许多行只是为了支持缺少专门测试异常处理的功能。 使用try / catch方法的一个好处是可以测试特定消息和预期异常上的任何自定义字段 。 我们将通过JUnit的ExpectedException和@Rule注释进一步探讨这一点。
JUnit添加了预期的异常
JUnit通过添加@Test注释字段“ expected”来响应用户对异常处理的需求。 目的是,如果引发的异常类型与注释中存在的异常类匹配,则整个测试用例将通过。
public class MyObjTest {@Test(expected = IllegalArgumentException.class)public void getNameWithNullValue() {MyObj obj = new MyObj();myObj.setName(null);}
}
从较新的示例中可以看到,样板代码要少得多,并且测试非常简洁,但是存在一些缺陷 。 主要缺陷是测试条件太宽泛。 假设签名中有两个变量,并且两个变量都不能为null,那么如何知道为IllegalArgumentException抛出了哪个变量呢? 当您扩展了Throwable并需要检查字段的存在时会发生什么? 在继续阅读时,请记住这些,随后将有解决方案。
JUnit @Rule和ExpectedException
如果查看前面的示例,可能会看到期望抛出IllegalArgumentException,但是如果您有一个自定义异常,该怎么办? 如果要确保该消息包含特定的错误代码或消息怎么办? 这是JUnit真正出色的地方,它提供了专门为异常测试量身定制的JUnit @Rule对象。 如果您不熟悉JUnit @Rule,请在此处阅读文档 。
ExpectedException
JUnit提供了一个JUnit类ExpectedException,该类旨在用作@Rule。 ExpectedException允许您的测试声明预期会出现异常,并为您提供一些基本的内置功能来清楚地表达预期的行为。 与@Test(expected)批注功能不同,ExpectedException类允许您通过Hamcrest匹配器库测试特定的错误消息和自定义字段。
JUnit的ExpectedException的示例
import org.junit.rules.ExpectedException;public class MyObjTest {@Rulepublic ExpectedException thrown = ExpectedException.none();@Testpublic void getNameWithNullValue() {thrown.expect(IllegalArgumentException.class);thrown.expectMessage('Name must not be null');MyObj obj = new MyObj();obj.setName(null);}
}
如前所述,该框架允许您测试特定消息,以确保在测试专门寻找的情况下抛出异常。 当怀疑多个参数的可空性时,这将非常有用。
自定义字段
可以说,ExpectedException框架最有用的功能是能够使用Hamcrest匹配器测试您的自定义/扩展异常。 例如,您有一个自定义/扩展的异常将被抛出到一个方法中,并且该异常内部有一个“ errorCode”。 如何在不从上面列出的try / catch块中引入样板代码的情况下测试该功能? 定制匹配器怎么样!
可以从以下网址获得此代码: https : //github.com/mike-ensor/custom-exception-testing
解决方案:首先是测试用例
import org.junit.rules.ExpectedException;public class MyObjTest {@Rulepublic ExpectedException thrown = ExpectedException.none();@Testpublic void someMethodThatThrowsCustomException() {thrown.expect(CustomException.class);thrown.expect(CustomMatcher.hasCode('110501'));MyObj obj = new MyObj();obj.methodThatThrowsCustomException();}
}
解决方案:自定义匹配器
import com.thepixlounge.exceptions.CustomException;
import org.hamcrest.Description;
import org.hamcrest.TypeSafeMatcher;public class CustomMatcher extends TypeSafeMatcher<CustomException> {public static BusinessMatcher hasCode(String item) {return new BusinessMatcher(item);}private String foundErrorCode;private final String expectedErrorCode;private CustomMatcher(String expectedErrorCode) {this.expectedErrorCode = expectedErrorCode;}@Overrideprotected boolean matchesSafely(final CustomException exception) {foundErrorCode = exception.getErrorCode();return foundErrorCode.equalsIgnoreCase(expectedErrorCode);}@Overridepublic void describeTo(Description description) {description.appendValue(foundErrorCode).appendText(' was not found instead of ').appendValue(expectedErrorCode);}
}
注意:请访问https://github.com/mike-ensor/custom-exception-testing以获取可用的Hamcrest Matcher,JUnit @Rule和ExpectedException的副本。
在那里,您可以快速概览一下测试代码引发的异常的不同方法,以及从自定义异常类中测试特定消息和字段的能力。 请具体说明您的测试用例,并尝试针对您为测试设置的确切用例,请记住,测试可以避免引入副作用漏洞!
祝您编程愉快,别忘了分享!
参考:在Mike的站点博客上,从JCG合作伙伴 Mike 那里 ,使用JUnit的ExpectedException和@Rule测试自定义异常 。
翻译自: https://www.javacodegeeks.com/2012/10/testing-custom-exceptions-with-junits.html