Bad form for JUnit test to throw exception?
我对JUnit很陌生,我真的不知道异常和异常处理的最佳实践。
例如,假设我正在为IPAddress类编写测试。 它有一个构造函数IPAddress(String addr),如果addr为null,它将抛出InvalidIPAddressException。 据我在google搜索时可以看出,null参数的测试将如下所示。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 | @Test public void testNullParameter() { try { IPAddress addr = new IPAddress(null); assertTrue(addr.getOctets() == null); } catch(InvalidIPAddressException e) { return; } fail("InvalidIPAddressException not thrown."); } |
在这种情况下,try / catch是有意义的,因为我知道异常即将到来。
但是现在如果我想编写testValidIPAddress(),有几种方法可以做到:
方式#1:
1 2 3 4 5 6 7 8 9 10 11 | @Test public void testValidIPAddress() throws InvalidIPAddressException { IPAddress addr = new IPAddress("127.0.0.1"); byte[] octets = addr.getOctets(); assertTrue(octets[0] == 127); assertTrue(octets[1] == 0); assertTrue(octets[2] == 0); assertTrue(octets[3] == 1); } |
方式#2:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 | @Test public void testValidIPAddress() { try { IPAddress addr = new IPAddress("127.0.0.1"); byte[] octets = addr.getOctets(); assertTrue(octets[0] == 127); assertTrue(octets[1] == 0); assertTrue(octets[2] == 0); assertTrue(octets[3] == 1); } catch (InvalidIPAddressException e) { fail("InvalidIPAddressException:" + e.getMessage()); } } |
标准做法是向JUnit抛出意外的异常还是自己处理它们?
谢谢您的帮助。
实际上,旧式的异常测试是在抛出异常的代码周围包装一个try块,然后在try块的末尾添加一个
1 2 3 4 5 6 7 8 | public void testNullParameter() { try { IPAddress addr = new IPAddress(null); fail("InvalidIPAddressException not thrown."); } catch(InvalidIPAddressException e) { assertNotNull(e.getMessage()); } } |
这和你写的没什么不同,但是:
不过,这有点难看。但这就是JUnit 4拯救的地方,因为异常测试是JUnit 4中最大的改进之一。使用JUnit 4,您现在可以像这样编写测试:
1 2 3 4 | @Test (expected=InvalidIPAddressException.class) public void testNullParameter() throws InvalidIPAddressException { IPAddress addr = new IPAddress(null); } |
不错,不是吗?
现在,关于真正的问题,如果我不期望抛出异常,我肯定会选择#1(因为它不那么详细)并让JUnit处理异常并按预期失败测试。
对于我不期望例外的测试,我不打算抓住它。我让JUnit捕获异常(它可靠地执行此操作)并且除了声明
我注意到了。你的第一个例子是你没有使用
1 2 3 4 5 | @Test (expected=IndexOutOfBoundsException.class) public void elementAt() { int[] intArray = new int[10]; int i = intArray[20]; // Should throw IndexOutOfBoundsException } |
我将此用于我正在测试抛出异常的所有测试。它比我必须与Junit3一起使用的等效捕获/失败模式更简洁。
从JUnit 4.7开始,您可以使用ExpectedException规则,并且应该使用它。该规则使您可以准确定义被调用的方法,其中应在测试代码中抛出异常。此外,您可以轻松地将字符串与异常的错误消息进行匹配。在您的情况下,代码如下所示:
1 2 3 4 5 6 7 8 9 | @Rule public ExpectedException expectedException = ExpectedException.none(); @Test public void test() { //working code here... expectedException.expect(InvalidIPAddressException.class); IPAddress addr = new IPAddress(null); } |
更新:在他的书中使用JUnit的实用单元测试和Mockito Tomek Kaczanowski反对使用ExpectedException,因为规则"打破了单元测试的安排/行为/断言流程"(他建议使用Catch Exception)而是图书馆)。虽然我可以理解他的论点,但我认为如果你不想引入另一个第三方库,使用该规则就好了(使用该规则比手动"捕获"异常更好)。
注册:测试例外情况
我同意"Pascal Thivent",即使用
注册:测试
1 2 | IPAddress addr = new IPAddress("127.0.0.1"); byte[] octets = addr.getOctets(); |
我会写一个类似的测试
1 2 3 4 5 6 7 8 9 10 | class IPAddressTests { [Test] public void getOctets_ForAValidIPAddress_ShouldReturnCorrectOctect() { // Test code here } } |
关键是testInput是VALID ipAddress
测试必须是关于类声明它们作为例外工作的类的公共方法/功能
一般来说,方式#1是要走的路,没有理由在错误上调出故障 - 无论哪种方式,测试基本上都失败了。
如果你需要一个关于出错的好消息,#2的唯一时间路是有意义的,只是一个例外不会给你。然后捕获和失败可以更好地宣布失败的原因。
如果我理解你的问题,答案是 - 个人偏好。
我亲自在测试中抛出异常。在我看来,通过断言失败的测试等同于未被捕获的异常导致的测试失败。两者都表明需要修复的东西。
在测试中要记住的重要事项是代码覆盖率。
对于null测试,您可以简单地执行此操作:
1 2 3 4 5 6 7 | public void testNullParameter() { try { IPAddress addr = new IPAddress(null); fail("InvalidIPAddressException not thrown."); } catch(InvalidIPAddressException e) { } } |
如果异常有消息,您也可以根据需要在catch中检查该消息。例如。
1 2 |
对于有效测试,您不需要捕获异常。如果抛出异常而未捕获异常,则测试将自动失败。所以#1的方式就是你所需要的,因为它会失败,无论如何你都可以使用堆栈跟踪来获得观赏乐趣。
IMO最好处理异常并从测试中显示适当的消息,而不是从测试中抛出它。