JUnittesting抛出exception的不良forms?
对于JUnit来说,我相当新,而且我不知道什么是最佳实践,例外和exception处理。
例如,假设我正在编写一个IPAddress类的testing。 它有一个构造函数IPAddress(String addr),如果addr为null,将会抛出一个InvalidIPAddressExceptionexception。 据我可以从谷歌search来看,空参数的testing看起来像这样。
@Test public void testNullParameter() { try { IPAddress addr = new IPAddress(null); assertTrue(addr.getOctets() == null); } catch(InvalidIPAddressException e) { return; } fail("InvalidIPAddressException not thrown."); }
在这种情况下,try / catch是有道理的,因为我知道exception即将到来。
但是现在如果我想写testValidIPAddress(),有几种方法可以做到这一点:
方法1:
@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:
@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意外的exception,或者只是自己处理它们?
谢谢您的帮助。
实际上,旧式的exceptiontesting是在引发exception的代码周围包装try块,然后在try块末尾添加一个fail()
语句。 像这样的东西:
public void testNullParameter() { try { IPAddress addr = new IPAddress(null); fail("InvalidIPAddressException not thrown."); } catch(InvalidIPAddressException e) { assertNotNull(e.getMessage()); } }
这和你写的没什么不同,但是:
- 你的
assertTrue(addr.getOctets() == null);
没用。 - 意图和语法更清晰的国际海事组织,因此更容易阅读。
不过,这有点难看。 但是,这正是JUnit 4开始救援的地方,因为exceptiontesting是JUnit 4中最大的改进之一。现在,您可以使用JUnit 4编写您的testing:
@Test (expected=InvalidIPAddressException.class) public void testNullParameter() throws InvalidIPAddressException { IPAddress addr = new IPAddress(null); }
很好,不是吗?
现在,关于真正的问题,如果我不期望抛出一个exception,我肯定会去#1(因为它不那么冗长),让JUnit处理exception,并按预期的方式通过testing。
对于那些我不期望有例外的testing,我不费心去理解它。 我让JUnit捕获exception(它可靠地做到这一点),除了声明throws
原因(如果需要的话)之外,根本不需要迎合它。
我注意到了。 你的第一个例子,你没有使用@expected
注释即,
@Test (expected=IndexOutOfBoundsException.class) public void elementAt() { int[] intArray = new int[10]; int i = intArray[20]; // Should throw IndexOutOfBoundsException }
我使用这个testing来testing抛出exception。 这比我必须使用Junit3的等效catch / fail模式要简单得多。
由于JUnit 4.7你有可能使用ExpectedException规则,你应该使用它。 该规则使您可以准确定义在testing代码中抛出exception的被调用方法。 而且,您可以轻松地将string与exception的错误消息进行匹配。 在你的情况下,代码如下所示:
@Rule public ExpectedException expectedException = ExpectedException.none(); @Test public void test() { //working code here... expectedException.expect(InvalidIPAddressException.class); IPAddress addr = new IPAddress(null); }
更新:在JUnit实践unit testing和Mockito Tomek Kaczanowski这本书中,反对使用ExpectedException,因为规则“打破了unit testing的安排/行为/断言stream程”(他build议使用Catch Exception图书馆 )。 虽然我可以理解他的论点,但我认为如果你不想引入另一个第三方库(使用规则比手动捕获exception要好),那么使用规则就没问题了。
对于空testing,你可以简单地这样做:
public void testNullParameter() { try { IPAddress addr = new IPAddress(null); fail("InvalidIPAddressException not thrown."); } catch(InvalidIPAddressException e) { } }
如果这个exception有消息,你也可以在catch中检查这个消息。 例如
String actual = e.getMessage(); assertEquals("Null is not a valid IP Address", actual);
对于有效的testing,您不需要捕捉exception。 如果引发exception并且未捕获,则testing将自动失败。 因此,#1的方式将是你所需要的,因为它会失败,堆栈跟踪将无论如何为您的观赏乐趣。
如果我理解你的问题,答案是 – 个人偏好。
个人我在testing中抛出我的例外。 在我看来,断言失败的testing相当于未被捕获的exception失败的testing。 都显示需要修复的东西。
在testing中要记住的重要的事情是代码覆盖率。
通常情况下,#1是要走的路,没有理由对错误进行失败 – 无论是testing本质上是失败的。
唯一的时间方式#2是有意义的,如果你需要一个好的信息出了问题,只是一个例外不会给你。 那么抓住失败就可以更好地宣布失败的原因。
注册:testing例外
我同意“Pascal Thivent”,即使用@Test (expected=InvalidIPAddressException.class)
Reg:testingtestValidIPAddress
IPAddress addr = new IPAddress("127.0.0.1"); byte[] octets = addr.getOctets();
我会写一个testing像
class IPAddressTests { [Test] public void getOctets_ForAValidIPAddress_ShouldReturnCorrectOctect() { // Test code here } }
关键是当testinput是有效的ipAddress
testing必须是在类的公共方法/能力,声称他们正在例外工作
国际海事组织(IMO)最好处理这个例外情况,并显示来自testing的适当消息,而不是从testing中抛出。