简体   繁体   English

如何在junit4 Java中测试多行代码是否引发异常

[英]How to test if multiple lines of code throw an exception in junit4 Java

I am writing a negative test for the method add(T elem, int index) of a class. 我正在为类的方法add(T elem, int index)写一个否定测试。 The method is expected to throw IllegalArgumentException if index < 0 || index > size || elem == null 如果index < 0 || index > size || elem == null则该方法应引发IllegalArgumentException index < 0 || index > size || elem == null index < 0 || index > size || elem == null . index < 0 || index > size || elem == null My test looks like this: 我的测试看起来像这样:

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

The test turns green, but I figured it out that it takes only 1 of these 3 lines of code to throw an exception in order for the test to turn green. 该测试变为绿色,但是我发现这三行代码中只有1个抛出一个异常即可使测试变为绿色。 So if I add 1 line to the code like this, the test still turns green. 因此,如果我向这样的代码中添加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 
}

So how I can make sure that the test tests if every single line throws an exception and not just one? 那么,如何确保测试测试每一行是否引发一个异常而不仅仅是一个异常?

The expected exception is expected in the scope of the whole test method execution whatever if some other statements don't throw it. 预期的异常是整个测试方法执行的范围,无论是否有其他语句不抛出异常。
So you should create a test method by possible scenario. 因此,您应该根据可能的情况创建一种测试方法。

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

While you could still use a single test method with as many try/catch statements as failing scenarios to test as shown in the SilverNak answer , I would not recommend it for readability reasons. 尽管仍然可以使用一个测试方法,但try/catch语句的数量与失败的场景一样多,如SilverNak答案所示,但出于可读性原因,我不建议您使用它。

Note that beyond your case, specifying each distinct scenario in its own method is a good practice as it makes tests more readable and it also makes simpler/easier to correct a test that fails as its responsibility is clearer and well defined. 请注意,除了您的情况外,在自己的方法中指定每个不同的场景是一个好习惯,因为它可以使测试更具可读性,并且由于其责任更加明确和明确,也使得更容易/更容易纠正失败的测试。


JUnit 5 improvements JUnit 5改进

Note that with the last release JUnit version, you could gather some common cases in a same method without decreasing the code readability. 请注意,使用最新发行的JUnit版本,您可以使用相同的方法收集一些常见情况,而不会降低代码的可读性。
You could gather the invalid cases with incorrect index passed to such as : 您可以收集传递给诸如以下错误索引的无效案例:

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

But I would keep this one in a separate method : 但我会将其保留在单独的方法中:

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

By writing four different test methods: 通过编写四种不同的测试方法:

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

If you use a plugin such as JaCoCo , you can visually confirm that all possible paths are covered. 如果使用JaCoCo之类的插件,则可以从视觉上确认是否覆盖了所有可能的路径。

An exception will terminate the method which is currently executed, until the exception is caught. 异常将终止当前执行的方法,直到捕获到异常为止。 So you have two possibilities: 因此,您有两种可能性:

One method for every test 每种测试一种方法

You can write one method for every scenario in which an exception should be thrown: 您可以为每种情况都应抛出异常的情况编写一种方法:

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

Catch exceptions yourself 自己捕获异常

You could also catch all exceptions yourself and fail the test if the exception is not thrown. 您也可以自己捕获所有异常,如果未引发异常,则测试失败。 When you use this method, you can verify additional properties of the exception (eg the message). 使用此方法时,可以验证异常的其他属性(例如,消息)。

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

If you do want to test additional properties of the thrown exception, you can choose the second method. 如果确实要测试引发的异常的其他属性,则可以选择第二种方法。 Otherwise, I recommend the first alternative as it is easier to understand and less code to write yourself. 否则,我建议第一种选择,因为它更易于理解,编写自己的代码更少。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM