如何在Junit4 Java中测试多个代码是否抛出异常



我正在为类的方法add(T elem, int index)编写负面测试。如果index < 0 || index > size || elem == null,该方法预计将投掷IllegalArgumentException。我的测试看起来像:

@Rule
public ExpectedException thrown = ExpectedException.none();
@Test(expected = IllegalArgumentException.class) 
public void addNegTest() {
    l0.add(42, 10);  // index > size 
    l0.add(42, -1);  // index < 0
    l0.add(null, 2); // elem == null
}

测试变绿色,但我发现这只需要这3行代码中的1个即可抛出例外,以使测试变成绿色。因此,如果我在这样的代码中添加1行,则测试仍然变绿。

@Test(expected = IllegalArgumentException.class) 
public void addNegTest() {
    l0.add(42, 10);  // index > size 
    l0.add(42, -1);  // index < 0
    l0.add(null, 2); // elem == null
    l0.add(42, 0);   // this is perfectly fine 
}

因此,我如何确保测试测试是否会引发异常,而不仅仅是一个。

the 测试方法执行的范围中,预期的异常是预期的。
因此,您应该在可能的情况下创建一种测试方法。

@Test(expected = IllegalArgumentException.class) 
public void add_with_index_greater_than_size() {
    l0.add(42, 10);  // index > size 
}  
@Test(expected = IllegalArgumentException.class) 
public void add_with_index_less_than_zero() {
    l0.add(42, -1);  
}  
@Test(expected = IllegalArgumentException.class) 
public void add_with_null_arg() {
    l0.add(null, 10); 
}  
@Test
public void add() {
    l0.add(42, 10);  
    Assert.assertEquals(42, l0.get(10));
}  

您仍然可以使用单个测试方法,其中包含与Silvernak答案所示的尽可能多的try/catch语句进行测试,但出于可读性原因,我不建议它。

请注意,除了您的情况之外,在自己的方法中指定每个不同的方案是一个很好的做法,因为它使测试更具可读性,并且更简单/更容易纠正失败的测试,因为其责任更加清晰且定义良好。


JUNIT 5改进

请注意,在最后一个版本的Junit版本中,您可以以相同的方法收集一些常见情况而不降低代码可读性。
您可以收集无效的案例,而错误的索引传递给了:

import org.junit.jupiter.api.Assertions;
import org.junit.jupiter.api.Test;
...
@Test
public void add_with_index_less_than_zero_or_greater_than_size() {
    Assertions.assertThrows(IllegalArgumentException.class, 
                             () -> l0.add(42, 10));
    Assertions.assertThrows(IllegalArgumentException.class, 
                             () -> l0.add(42, -1));
}  

,但我会将这一方法保留在单独的方法中:

import  org.junit.jupiter.api.Assertions;
import org.junit.jupiter.api.Test;
...
@Test
public void add_with_null_arg() {
    Assertions.assertThrows(IllegalArgumentException.class, 
                             () -> l0.add(null, 10));
}  

通过编写四种不同的测试方法:

@Test(expected = IllegalArgumentException.class) 
public void addNegTest_indexTooLarge() {
    l0.add(42, 10);  // index > size 
}
@Test(expected = IllegalArgumentException.class) 
public void addNegTest_negativeIndex() {
    l0.add(42, -1);  // index < 0
}
@Test(expected = IllegalArgumentException.class) 
public void addNegTest_nullElement() {
    l0.add(null, 2); // elem == null
}
@Test
public void addNegTest_ok() {
    l0.add(42, 0);   // this is perfectly fine 
}

如果您使用诸如jacoco之类的插件,则可以在视觉上确认所有可能的路径均已涵盖。

一个异常将终止当前执行的方法,直到抓住例外为止。因此,您有两种可能性:

每种测试的一种方法

您可以为应抛出异常的每种情况编写一种方法:

@Test(expected = IllegalArgumentException.class) 
public void addIndexGreaterThanSize() {
    l0.add(42, 10);  // index > size 
}
@Test(expected = IllegalArgumentException.class) 
public void addIndexNegative() {
    l0.add(42, -1);  // index < 0
}
@Test(expected = IllegalArgumentException.class) 
public void addElementNull() {
    l0.add(null, 2); // elem == null
}

捕获异常

您也可以亲自捕获所有例外,如果没有抛出异常,则无法通过测试失败。使用此方法时,您可以验证异常的其他属性(例如,消息(。

@Test
public void addNegTest() {
    try {
        l0.add(42, 10);  // index > size 
        fail();
    } catch (IllegalArgumentException e) {}
    try {
        l0.add(42, -1);  // index < 0
        fail();
    } catch (IllegalArgumentException e) {}
    try {
        l0.add(null, 2); // elem == null
        fail();
    } catch (IllegalArgumentException e) {}
}

如果您确实要测试抛出异常的其他属性,则可以选择第二种方法。否则,我建议您使用第一个选择,因为它更容易理解,而更少的代码来写信。

最新更新