【问题标题】:How to test if multiple lines of code throw an exception in junit4 Java如何在junit4 Java中测试多行代码是否抛出异常
【发布时间】:2018-04-29 11:11:10
【问题描述】:

我正在为一个类的方法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 
}

那么我如何确保测试测试是否每一行都抛出异常而不仅仅是一个?

【问题讨论】:

  • 您不使用ExpectedException 规则(您可以省略它)。您的代码和异常规则会有一个解决方案,但我更喜欢“每个场景一个测试”的方法(请参阅答案)。

标签: java unit-testing exception junit automated-tests


【解决方案1】:

预期的异常应该在整个测试方法执行的范围内,如果其他一些语句没有抛出它。
所以你应该根据可能的场景创建一个测试方法。

@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));
}  

虽然您仍然可以使用单个测试方法,其中包含与 the SilverNak answer 中所示的失败场景一样多的 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));
}  

【讨论】:

  • 你应该在最后一个方法中添加某种断言
  • 我正在编辑 :) 非常感谢您的建议。
【解决方案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之类的插件,你可以直观地确认所有可能的路径都被覆盖了。

【讨论】:

  • 你应该在最后一个方法中添加某种断言
【解决方案3】:

异常会终止当前正在执行的方法,直到异常被捕获。所以你有两种可能:

每个测试都使用一种方法

您可以为每个应该引发异常的场景编写一个方法:

@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) {}
}

如果您确实想测试抛出异常的附加属性,您可以选择第二种方法。否则,我推荐第一个替代方案,因为它更容易理解并且自己编写的代码更少。

【讨论】:

    猜你喜欢
    • 2021-02-26
    • 1970-01-01
    • 1970-01-01
    • 2019-11-26
    • 1970-01-01
    • 2021-06-20
    • 1970-01-01
    • 1970-01-01
    • 2016-06-16
    相关资源
    最近更新 更多