了解 JUnit 5 中的异常测试
Understanding Exception tests in JUnit 5
我离开 Java 有一段时间了,同时 JUnit 5 出现了。我正在尝试将一些现有的 JUnit 4 测试重写为 JUnit 5,但我正在努力解决如何处理抛出异常的方法。例如,我有一个我写的名为 LocalizationUtils 的 class(不要与 API 中的任何其他 class 混淆)并且它有一个名为 getResources() 的方法想要一个非空的基本名称作为输入,并且 returns 如果可以找到相应的资源包。如果基本名称为 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());
}
这个例子对我来说毫无意义。它似乎凭空创建了消息 "a message" 的异常。我没有看到它实际上正在执行任何 class 或方法。没有关于它应该如何工作的真正解释,所以 maybe/probably 这只是我的误解。
为了尝试编写一个实际执行我的代码并强制发生这两个错误的测试,我编写了这个 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());
}
这个测试中的一切都按我预期的那样工作,它看起来比手册中的例子更合乎逻辑,因为它实际上执行了一个 class 和方法来导致抛出异常。我还想确保生成了完全正确的消息,因为很容易想象一个具有多个输入参数的方法,而不是只有一个 any 参数为 null 的方法应该抛出异常一个独特的消息。仅仅确定抛出了正确的异常对我来说似乎还不够:我觉得我想确保创建了正确的消息,以便我知道代码正在对几个正确的 null 参数做出反应。
虽然我不愿意相信手册是错误的;我想象一个由几位经验丰富的开发人员组成的团队编写了它并且非常小心。任何比我拥有更多 JUnit 5 知识的人——肯定是几乎所有使用过 JUnit 5 的人——都可以确认手册中的示例是正确的,如果是的话,它应该如何工作时没有 class 或提供方法名称?
该示例确实调用了 "method"。 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); }
我离开 Java 有一段时间了,同时 JUnit 5 出现了。我正在尝试将一些现有的 JUnit 4 测试重写为 JUnit 5,但我正在努力解决如何处理抛出异常的方法。例如,我有一个我写的名为 LocalizationUtils 的 class(不要与 API 中的任何其他 class 混淆)并且它有一个名为 getResources() 的方法想要一个非空的基本名称作为输入,并且 returns 如果可以找到相应的资源包。如果基本名称为 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());
}
这个例子对我来说毫无意义。它似乎凭空创建了消息 "a message" 的异常。我没有看到它实际上正在执行任何 class 或方法。没有关于它应该如何工作的真正解释,所以 maybe/probably 这只是我的误解。
为了尝试编写一个实际执行我的代码并强制发生这两个错误的测试,我编写了这个 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());
}
这个测试中的一切都按我预期的那样工作,它看起来比手册中的例子更合乎逻辑,因为它实际上执行了一个 class 和方法来导致抛出异常。我还想确保生成了完全正确的消息,因为很容易想象一个具有多个输入参数的方法,而不是只有一个 any 参数为 null 的方法应该抛出异常一个独特的消息。仅仅确定抛出了正确的异常对我来说似乎还不够:我觉得我想确保创建了正确的消息,以便我知道代码正在对几个正确的 null 参数做出反应。
虽然我不愿意相信手册是错误的;我想象一个由几位经验丰富的开发人员组成的团队编写了它并且非常小心。任何比我拥有更多 JUnit 5 知识的人——肯定是几乎所有使用过 JUnit 5 的人——都可以确认手册中的示例是正确的,如果是的话,它应该如何工作时没有 class 或提供方法名称?
该示例确实调用了 "method"。 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); }