我正在为类的方法add(T elem, int index)
编写负面测试。如果IllegalArgumentException
,该方法应该抛出index < 0 || index > size || elem == null
。我的测试看起来像这样:
@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
}
测试变为绿色,但我发现这三行代码中只有一行会抛出异常以使测试变为绿色。因此,如果我像这样在代码中添加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
}
那么我怎样才能确保测试测试是否每一行抛出一个异常而不只是一个?
答案 0 :(得分:6)
预期的异常应该在整个测试方法执行的范围内,无论其他一些语句是否抛弃它。
因此,您应该根据可能的方案创建测试方法。
@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));
}
虽然您仍然可以使用单个测试方法,尽可能多try/catch
个语句作为失败方案进行测试,如the SilverNak answer所示,但出于可读性原因,我不建议这样做。
请注意,除了您的情况之外,在其自己的方法中指定每个不同的场景是一个很好的做法,因为它使测试更具可读性,并且还使得更简单/更容易纠正失败的测试,因为它的职责更清晰,定义更明确。
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));
}
答案 1 :(得分:2)
编写四种不同的测试方法:
@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等插件,则可以直观地确认所有可能的路径都已被覆盖。
答案 2 :(得分:2)
异常将终止当前执行的方法,直到捕获到异常。所以你有两种可能性:
每次测试的一种方法
您可以为应该抛出异常的每个场景编写一个方法:
@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) {}
}
如果确实要测试抛出异常的其他属性,可以选择第二种方法。否则,我推荐第一个替代方案,因为它更容易理解,更少的代码可以自己编写。