【问题标题】:Understanding Exception tests in JUnit 5了解 JUnit 5 中的异常测试
【发布时间】:2018-10-15 21:36:01
【问题描述】:

我已经离开 Java 一段时间了,与此同时 JUnit 5 也出现了。我正在尝试将一些现有的 JUnit 4 测试重写为 JUnit 5,但我正在努力解决如何处理引发异常的方法。例如,我有一个我写的名为 LocalizationUtils 的类(不要与在 API 中找到的该名称的任何其他类混淆),它有一个名为 getResources() 的方法,它需要一个非空的基本名称作为输入并返回如果可以找到相应的资源包。如果基本名称为 null,则抛出 NullPointerException;如果找不到资源包,则抛出 MissingResourceException。代码如下:

    static public ResourceBundle getResources(String baseName) throws NullPointerException, MissingResourceException {

    if (baseName == null) {
        final String msg = "The base name cannot be null.";
        NullPointerException npExcp = new NullPointerException(msg); 
        Logger logger = Logger.getLogger(CLASS_NAME);
        logger.log(Level.SEVERE, msg, npExcp);
        throw npExcp;
        }

    /* Get the resource bundle for the current locale. */
    try {
        return(ResourceBundle.getBundle(baseName));
        } 
    catch (MissingResourceException mrExcp) {
        String msg = "Unable to find resources for base name " + baseName + "."; 
        Logger logger = Logger.getLogger(CLASS_NAME);
        logger.log(Level.SEVERE, msg, mrExcp); 
        throw mrExcp;
        }

}   

JUnit 5 的手册给出了这个处理异常的例子:

@Test
void exceptionTesting() {
    Throwable exception = assertThrows(IllegalArgumentException.class, () -> {
        throw new IllegalArgumentException("a message");
    });
    assertEquals("a message", exception.getMessage());
}

这个例子对我来说毫无意义。它似乎是凭空产生了“一条消息”消息的异常。我根本看不到它实际上正在执行任何类或方法。没有真正解释这应该如何工作,所以也许/可能这只是我的一个误解。

为了编写一个实际执行我的代码并强制两个错误发生的测试,我编写了这个 JUnit 5 测试:

@Test
void testGetResourcesString() {

    //Normal cases

    //Exception - input parameter is null
    /* Verify that the expected type of exception was thrown. */
    Throwable npExcp = assertThrows(NullPointerException.class, () -> {
        LocalizationUtils.getResources(null);
    });   
    /* Verify that the exact right error message was generated. */   
    assertEquals("The base name cannot be null.", npExcp.getMessage()); 


    //Exception - all input is non-null but resource bundle not found
    /* Verify that the expected type of exception was thrown. */
    Throwable mrExcp = assertThrows(MissingResourceException.class, () -> {
        LocalizationUtils.getResources("foo");
    });   
    /* Verify that the exact right error message was generated. */
    assertEquals("Can't find bundle for base name foo, locale en_CA", mrExcp.getMessage());     
}

此测试中的所有内容都按我的预期工作,并且它似乎比手册中的示例更合乎逻辑,因为它实际上执行了一个类和方法来引发异常。我还想确保生成了完全正确的消息,因为很容易想象一个具有多个输入参数的方法,而不是只有一个参数为空的 any 应该抛出带有唯一消息的异常.仅仅确定抛出了正确的异常对我来说似乎还不够:我觉得我想确保创建了正确的消息,以便我知道代码正在对几个正确的 null 参数做出反应。

不过,我不愿意相信手册有误;我想一个由几位经验丰富的开发人员组成的团队编写了它并且非常小心。任何比我了解更多 JUnit 5 知识的人(应该是几乎所有曾经使用过 JUnit 5 的人)都可以确认手册中的示例是否正确,如果正确,那么在没有类或方法名称时它应该如何工作提供?

【问题讨论】:

  • 您无中生有地创建异常是正确的。它相当于在测试方法中有expected = IllegalArgumentException 并抛出IllegalArgumentException。唯一的区别是,断言允许您直接检查异常消息(与 JUnit4 不同,您必须创建 @Rule
  • 在您看来,我的 JUnit 测试代码是对我的 getResource() 方法的公平和充分的测试吗?
  • 是的,你测试正确
  • 你对assertThrows()的使用是正确的!对于用户指南中不切实际的示例,我深表歉意。我已经打开了new issue 来解决这个缺点。
  • @Sam Brannen - 感谢您确认我对如何使用 assertThrows() 有正确的理解。我很确定我走在正确的轨道上,但确认总是好的。非常感谢您采取措施实际改进文档!很多时候,我发现文档中的缺点并且不知道如何提出改进建议。 (我的愤世嫉俗的部分想知道是否有任何意义,即使我可以确定如何。)我可以建议进一步改进吗?我正在使用 Eclipse 2018-2019,它还没有提供 JUnit5 测试套件 - 显然没有意图....

标签: java exception junit5


【解决方案1】:

该示例确实调用了“方法”。 lambda 表达式() -> {...} 定义了一个匿名方法。它包含一条语句:throw new IllegalArgumentException

Refer to the java language specs

() -> {}                // No parameters; result is void
() -> 42                // No parameters, expression body
() -> null              // No parameters, expression body
() -> { return 42; }    // No parameters, block body with return
() -> { System.gc(); }  // No parameters, void block body
(int x) -> x+1              // Single declared-type parameter
(int x) -> { return x+1; }  // Single declared-type parameter
(x) -> x+1                  // Single inferred-type parameter
 x  -> x+1                  // Parens optional for single inferred-type case

所以这个例子只有一个方法,它所做的只是抛出一个异常。

() -> { throw new IllegalArgumentException("a message"); }

你的代码中,你实际上是在定义一个没有参数的方法,用一个参数调用你的方法。

() -> { LocalizationUtils.getResources(null); } 

【讨论】:

  • 好吧,我的措辞并不准确:lambda 表达式并不像我描述的那样“稀薄”。但我认为我的基本观点 仍然准确:示例中的代码不测试任何外部类和方法,而这实际上是在 JUnit 测试中完成的。这就是 JUnit 的重点:测试外部类和方法。如果它不执行外部类和方法,它就不能测试外部类和方法。我想你会同意的。那么我编写测试的方式是有效的测试吗?
  • 嗯,单元测试的重点是断言无论你调用什么都会产生你期望的输出。无论您是想粘贴一个外部类并在其中调用它的方法,还是只想检查某些代码在 Java 中的行为方式,都取决于您。在这种情况下,他们提供了一个测试方法的示例,该示例包含一个语句(恰好是throw 语句),并显示了它会发生什么。如果您放置一个抛出异常的类方法,您可以期望它的行为类似(您已经完成了,并通过了有效的测试:))。
猜你喜欢
  • 1970-01-01
  • 2011-01-28
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2013-02-19
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多