Java JUnit测试引发异常的错误形式?

Java JUnit测试引发异常的错误形式?,java,unit-testing,testing,junit,Java,Unit Testing,Testing,Junit,我对JUnit非常陌生,我不知道异常和异常处理的最佳实践是什么 例如,假设我正在为一个IPAddress类编写测试。它有一个构造函数IPAddress(String addr),如果addr为null,它将抛出InvalidIPAddressException。从谷歌搜索中我可以看出,null参数的测试如下所示 @Test public void testNullParameter() { try { IPAddress addr = new IPAddress(

我对JUnit非常陌生,我不知道异常和异常处理的最佳实践是什么

例如,假设我正在为一个IPAddress类编写测试。它有一个构造函数IPAddress(String addr),如果addr为null,它将抛出InvalidIPAddressException。从谷歌搜索中我可以看出,null参数的测试如下所示

@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:

道路#2:

标准做法是向JUnit抛出意外异常,还是自己处理


感谢您的帮助。

对于空测试,您只需执行以下操作:

public void testNullParameter() {
    try {
            IPAddress addr = new IPAddress(null);
            fail("InvalidIPAddressException not thrown.");
    }
    catch(InvalidIPAddressException e) { }
}
如果异常有一条消息,如果愿意,也可以在catch中检查该消息。例如

String actual = e.getMessage();
assertEquals("Null is not a valid IP Address", actual);

对于有效的测试,您不需要捕获异常。如果抛出异常且未捕获,测试将自动失败。因此,方法#1将是您所需要的,因为它将失败,堆栈跟踪将为您提供查看乐趣。

如果我理解您的问题,答案是-个人偏好

就个人而言,我在测试中抛出异常。在我看来,断言导致的测试失败等同于未捕获异常导致的测试失败。两者都显示了一些需要修复的问题


在测试中要记住的重要一点是代码覆盖率。

对于我不希望出现异常的测试,我不会费心捕捉它。我让JUnit捕获异常(它可以可靠地执行此操作),并且除了声明
抛出
原因(如果需要)之外,根本不满足它

我注意到了你。您没有使用
@expected
注释viz的第一个示例

@Test (expected=IndexOutOfBoundsException.class) public void elementAt() {
    int[] intArray = new int[10];

    int i = intArray[20]; // Should throw IndexOutOfBoundsException
  }

我在测试抛出异常的所有测试中都使用这个。它比我必须在Junit3中使用的等效catch/fail模式更简单。

实际上,异常测试的旧式方法是在抛出异常的代码周围包装一个try块,然后在try块的末尾添加一个
fail()
语句。大概是这样的:

public void testNullParameter() {
    try {
        IPAddress addr = new IPAddress(null);
        fail("InvalidIPAddressException not thrown.");
    } catch(InvalidIPAddressException e) {
        assertNotNull(e.getMessage());
    }
}
@Test (expected=InvalidIPAddressException.class) 
public void testNullParameter() throws InvalidIPAddressException {
    IPAddress addr = new IPAddress(null);
}
    @Rule
    public ExpectedException expectedException = ExpectedException.none();

    @Test
    public void test() {
        //working code here...
        expectedException.expect(InvalidIPAddressException.class);
        IPAddress addr = new IPAddress(null);
    }
这和你写的没什么不同,但是:

  • 您的
    assertTrue(addr.getOctets()==null)是无用的
  • 在我看来,意图和语法更清晰,因此更容易阅读
  • 不过,这有点难看。但这正是JUnit4得以拯救的地方,因为异常测试是JUnit4最大的改进之一。使用JUnit 4,您现在可以这样编写测试:

    public void testNullParameter() {
        try {
            IPAddress addr = new IPAddress(null);
            fail("InvalidIPAddressException not thrown.");
        } catch(InvalidIPAddressException e) {
            assertNotNull(e.getMessage());
        }
    }
    
    @Test (expected=InvalidIPAddressException.class) 
    public void testNullParameter() throws InvalidIPAddressException {
        IPAddress addr = new IPAddress(null);
    }
    
        @Rule
        public ExpectedException expectedException = ExpectedException.none();
    
        @Test
        public void test() {
            //working code here...
            expectedException.expect(InvalidIPAddressException.class);
            IPAddress addr = new IPAddress(null);
        }
    
    很好,不是吗

    现在,关于真正的问题,如果我不希望抛出异常,我肯定会选择方法1(因为它不那么冗长),让JUnit处理异常并按预期通过测试。

    一般来说,方法是1,没有理由为错误调用失败-无论哪种方法,测试基本上都失败了


    如果你需要一个关于出错原因的好消息,那么唯一的时间方法#2是有意义的,而仅仅是一个例外不会给你这个信息。然后,捕获和失败可以更好地宣布失败的原因。

    在我看来,处理异常并从测试中显示适当的消息比从测试中抛出消息更好。

    因为JUnit 4.7,您可以使用规则,您应该使用它。该规则使您能够准确地定义被调用的方法,在该方法中,异常应该在测试代码中抛出。此外,您可以轻松地将字符串与异常的错误消息进行匹配。在您的情况下,代码如下所示:

    public void testNullParameter() {
        try {
            IPAddress addr = new IPAddress(null);
            fail("InvalidIPAddressException not thrown.");
        } catch(InvalidIPAddressException e) {
            assertNotNull(e.getMessage());
        }
    }
    
    @Test (expected=InvalidIPAddressException.class) 
    public void testNullParameter() throws InvalidIPAddressException {
        IPAddress addr = new IPAddress(null);
    }
    
        @Rule
        public ExpectedException expectedException = ExpectedException.none();
    
        @Test
        public void test() {
            //working code here...
            expectedException.expect(InvalidIPAddressException.class);
            IPAddress addr = new IPAddress(null);
        }
    

    更新:在他的书中,Tomek Kaczanowski反对使用ExpectedException,因为该规则“破坏了单元测试的排列/动作/断言[…]流程”(他建议改为使用)。虽然我能理解他的论点,但如果您不想引入另一个第三方库,我认为使用规则是可以的(无论如何,使用规则比“手动”捕获异常要好)。

    Reg:测试异常
    我同意“Pascal Thivent”,即使用
    @Test(预期=InvalidIPAddressException.class)


    注册:测试
    testValidIPAddress

    IPAddress addr = new IPAddress("127.0.0.1");
    byte[] octets = addr.getOctets();
    
    我会像这样写一个测试

    class IPAddressTests
    {
    
        [Test]
        public void getOctets_ForAValidIPAddress_ShouldReturnCorrectOctect()
        {
             // Test code here
        }
    
    }
    
    关键是testinput是有效的IP地址

    测试必须在类上的公共方法/功能上进行,该类声明它们作为例外工作

    我实际尝试了testNullParameter()的@test(expected=InvalidIPAddressException.class),并且测试在eclipse中返回了错误。你有没有遇到过这种情况?嗯,我刚刚试过JUnit4和Eclipse,测试运行得很好。但是,我忘记了答案中的throws子句,如果
    InvalidIPAddressException
    是选中的异常,则该子句是必需的。我相应地修改了我的答案。这有帮助吗?没有。不过我可能会发布一个不同的问题。感谢您的帮助。@Test(expected=…)表单实际上可能比显式的try/catch更糟糕,例如,如果测试的“setup”部分可以抛出预期的异常。您将丢失错误定位,即使已执行的方法停止抛出正确的异常,您的测试也可以保持绿色。如果在上下文中这是一件有趣的事情,那么try-catch方法将允许您测试抛出的异常的特征。此解决方案为测试添加了更多的样板文件,几乎没有任何好处。让JUnit通过将有问题的测试标记为错误来处理意外异常;如果测试的成功结果取决于引发特定异常的测试主题,请使用测试注释的“expected”属性,或使用ExpectedException JUnit规则。测试应设计为多次运行。测试的最大优点是当它们被自动化时,例如:每次您将代码签入源代码管理时,测试都可以运行。所以这里有很多