带有异常期望的 JUnit 测试(多个断言)

Posted

技术标签:

【中文标题】带有异常期望的 JUnit 测试(多个断言)【英文标题】:JUnit testing with exception expectations (multiple asserts) 【发布时间】:2012-01-24 14:30:00 【问题描述】:

我正在测试一个供 Xalan 使用的 WeekConverter,并且想知道我的测试到底在做什么。 :D

具有以下测试方法:

@Test(expected = IllegalArgumentException.class)
  public void testConvertTwoDigitYearWithWrongInput() 
  WeekConverter weekConverter = new WeekConverter(WeekConverter.Strategy.TWO_DIGIT_YEAR);

  //wrong or empty inputs
  assertEquals("0", weekConverter.convert(""));
  assertEquals("0", weekConverter.convert("abcdefgh"));

这个测试会期望所有断言的异常,还是只针对第一个断言?如果只是第一个,这意味着我必须为每个断言创建一个测试方法,尽管我期望在这两种情况下都会出现相同的异常。有人可以在这里确认我的例子吗?

我还有一个 null 测试,它会产生 NullPointerException。软验证如下:

if (inputDate == null) 
  do something and throw NullPointerexception
 else if (inputDate.isEmpty()) 
  do something and throw IllegalArgumentException, since inputDate is not really null
 else if (inputDate.matches(regex)) 
  go futher and convert
 else 
  do something and throw IllegalArgumentException, since inputDate does not match regex

因此,一个测试方法期望 IllegalArgumentException 带有两个断言。但很明显,我需要两种不同的测试方法,不仅要尊重 JUnit 的功能,而且我希望从两种不同的状态抛出。

【问题讨论】:

因为你期待一个异常,你不需要断言等于——你只需要调用转换。 【参考方案1】:

您可以将您的方法分解为多个方法,但如果您有很多输入样本,那就不方便了。

您可以改用以下方法:

@Test
public void testConvertTwoDigitYearWithWrongInput() 
    WeekConverter weekConverter = new WeekConverter(WeekConverter.Strategy.TWO_DIGIT_YEAR); 

    assertFailsToConvert(weekConverter, ""); 
    assertFailsToConvert(weekConverter, "abcdefgh");


private void assertFailsToConvert(WeekConverter weekConverter, String input) 
    try 
        weekConverter.convert(input);
        fail("Should not convert [" + input + "]");
     catch (IllegalArgumentException ex) 

【讨论】:

这样做的问题是,如果不查看测试,测试就不再是自记录的,并且如果没有 AST 和猜测,就不能再以人类可读的形式记录。将它们分开可以更好地生成信息。 但是对不同的输入样本进行大量单独的测试会影响可读性和可维护性,因此您应该决定什么对您更重要。【参考方案2】:

您应该提供多种测试方法,因为它们测试的是不同的东西。

转换器第一次得到非法参数时会抛出异常。

您还应该测试空输入,以记录行为。

【讨论】:

这是一个很好的观点......特别是在抛出相同的异常但来自不同的状态时。【参考方案3】:

测试只是期望 IllegalArgumentException 被抛出,不管它是从哪里抛出的或为什么抛出。

我建议你把它分成两个测试。

【讨论】:

【参考方案4】:

您可以将转换夹具的创建放在单独的@Before setup 方法中,然后您可以有(三个)单独的测试用例来处理null、“”和“abcdef”。

如果有更多的案例需要测试, JUnit 中一个简洁的方法是使用@Parameters 注释和相应的运行器。

您的测试课程将只处理不正确的两位数年份。它的构造函数将使用 String 类型的 inputDate 参数化。

产生@Parameters 的静态方法将返回一个包含""abcdefg 的集合(以及其他有趣的情况)。

单个测试用例需要IllegalArgumentException

@RunWith(Parameterized.class)
public class IncorrectTwoDigitYears 
    String inputDate;

    public IncorrectTwoDigitYears(String inputDate) 
        this.inputDate = inputDate;
    

    @Test(expected = IllegalArgumentException.class)
    public void testFormat() 
        (new WeekConverter(WeekConverter.Strategy.TWO_DIGIT_YEAR))
            .convert(inputDate);
    

    @Parameters
    public static Collection<Object[]> data() 
       Object[][] data = new Object[][]  
            "" ,  "abcdef" ,  "0" ,  "000" ,  "##"  ;
       return Arrays.asList(data);
    

如果您要测试的案例不止两个,回报会更高。

【讨论】:

【参考方案5】:

试试catch-exception:

@Test
public void testConvertTwoDigitYearWithWrongInput() 

    WeekConverter weekConverter = ...

    // wrong or empty inputs
    verifyException(weekConverter, IllegalArgumentException.class)
       .convert("");
    verifyException(weekConverter, IllegalArgumentException.class)
       .convert("abcdefgh");

【讨论】:

以上是关于带有异常期望的 JUnit 测试(多个断言)的主要内容,如果未能解决你的问题,请参考以下文章

JUnit 5:如何断言抛出异常?

Junit 测试断言错误预期为 3,但附带 0

如何使用 JUnit Test 注释断言我的异常消息?

Junit单元测试

Junit中的异常测试

Junit - 期望异常测试(Expected Test)