在 Java 中,如何使用 JUnit 验证抛出的异常?
声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow
原文地址: http://stackoverflow.com/questions/785618/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me):
StackOverFlow
In Java how can I validate a thrown exception with JUnit?
提问by Iain
When writing unit tests for a Java API there may be circumstances where you want to perform more detailed validation of an exception. I.e. more than is offered by the @testannotation offered by JUnit.
在为 Java API 编写单元测试时,您可能希望对异常执行更详细的验证。即比JUnit提供的@test注释提供的更多。
For example, consider an class that should catch an exception from some other Interface, wrap that exception and throw the wrapped exception. You may want to verify:
例如,考虑一个类应该从其他接口捕获异常,包装该异常并抛出包装的异常。您可能需要验证:
- The exact method call that throws the wrapped exception.
- That the wrapper exception has the original exception as its cause.
- The message of the wrapper exception.
- 引发包装异常的确切方法调用。
- 包装器异常将原始异常作为其原因。
- 包装器异常的消息。
The main point here is that you want to be perf additional validation of an exception in a unit test (not a debate about whether you shouldverify things like the exception message).
这里的要点是您希望在单元测试中对异常进行额外的验证(而不是关于是否应该验证异常消息之类的内容的争论)。
What's a good approach for this?
对此有什么好方法?
采纳答案by Jonas
In JUnit 4 it can be easily done using ExpectedExceptionrule.
在 JUnit 4 中,可以使用ExpectedException规则轻松完成。
Here is example from javadocs:
这是来自 javadocs 的示例:
// These tests all pass.
public static class HasExpectedException {
@Rule
public ExpectedException thrown = ExpectedException.none();
@Test
public void throwsNothing() {
// no exception expected, none thrown: passes.
}
@Test
public void throwsNullPointerException() {
thrown.expect(NullPointerException.class);
throw new NullPointerException();
}
@Test
public void throwsNullPointerExceptionWithMessage() {
thrown.expect(NullPointerException.class);
thrown.expectMessage("happened?");
thrown.expectMessage(startsWith("What"));
throw new NullPointerException("What happened?");
}
}
回答by Iain
The following helper method (adapted from thisblog post) does the trick:
下面的辅助方法(改编自这篇博文)可以解决这个问题:
/**
* Run a test body expecting an exception of the
* given class and with the given message.
*
* @param test To be executed and is expected to throw the exception.
* @param expectedException The type of the expected exception.
* @param expectedMessage If not null, should be the message of the expected exception.
* @param expectedCause If not null, should be the same as the cause of the received exception.
*/
public static void expectException(
Runnable test,
Class<? extends Throwable> expectedException,
String expectedMessage,
Throwable expectedCause) {
try {
test.run();
}
catch (Exception ex) {
assertSame(expectedException, ex.getClass());
if (expectedMessage != null) {
assertEquals(expectedMessage, ex.getMessage());
}
if (expectedCause != null) {
assertSame(expectedCause, ex.getCause());
}
return;
}
fail("Didn't find expected exception of type " + expectedException.getName());
}
The test code can then invoke this as follows:
然后测试代码可以调用它,如下所示:
TestHelper.expectException(
new Runnable() {
public void run() {
classInstanceBeingTested.methodThatThrows();
}
},
WrapperException.class,
"Exception Message",
causeException
);
回答by guerda
As provided in your answer, it's a good approach. In addition to this:
正如您的回答所提供的那样,这是一个很好的方法。除此之外:
You could wrap the function expectException
into a new Annotation, called ExpectedException
.
An annotated method would look like this:
您可以将该函数包装expectException
到一个名为ExpectedException
.
带注释的方法如下所示:
@Test
@ExpectedException(class=WrapperException.class, message="Exception Message", causeException)
public void testAnExceptionWrappingFunction() {
//whatever you test
}
This way would be more readable, but it's exactly the same approach.
这种方式会更具可读性,但它是完全相同的方法。
Another reason is: I like Annotations :)
另一个原因是:我喜欢注释:)
回答by mP.
For JUNIT 3.x
对于 JUNIT 3.x
public void test(){
boolean thrown = false;
try{
mightThrowEx();
} catch ( Surprise expected ){
thrown = true;
assertEquals( "message", expected.getMessage());
}
assertTrue(thrown );
}
回答by Esko
Until this post I've done my exception validation by doing this:
在这篇文章之前,我已经通过这样做完成了我的异常验证:
try {
myObject.doThings();
fail("Should've thrown SomeException!");
} catch (SomeException e) {
assertEquals("something", e.getSomething());
}
I spent a few moments thinking about the issue though and came up with the following (Java5, JUnit 3.x):
我花了一些时间思考这个问题,并提出了以下内容(Java5、JUnit 3.x):
// Functor interface for exception assertion.
public interface AssertionContainer<T extends Throwable> {
void invoke() throws T;
void validate(T throwable);
Class<T> getType();
}
// Actual assertion method.
public <T extends Throwable> void assertThrowsException(AssertionContainer<T> functor) {
try {
functor.invoke();
fail("Should've thrown "+functor.getType()+"!");
} catch (Throwable exc) {
assertSame("Thrown exception was of the wrong type! Expected "+functor.getClass()+", actual "+exc.getType(),
exc.getClass(), functor.getType());
functor.validate((T) exc);
}
}
// Example implementation for servlet I used to actually test this. It was an inner class, actually.
AssertionContainer<ServletException> functor = new AssertionContainer<ServletException>() {
public void invoke() throws ServletException {
servlet.getRequiredParameter(request, "some_param");
}
public void validate(ServletException e) {
assertEquals("Parameter \"some_param\" wasn't found!", e.getMessage());
}
public Class<ServletException> getType() {
return ServletException.class;
}
}
// And this is how it's used.
assertThrowsException(functor);
Looking at these two I can't decide which one I like more. I guess this is one of those issues where achieving a goal (in my case, the assertion method with functor parameter) isn't worth it in the long run since it's just a lot easier to do those 6+ of code to assert the try..catch block.
看着这两个,我无法决定我更喜欢哪一个。我想这是实现目标(在我的情况下,具有函子参数的断言方法)从长远来看不值得的问题之一,因为执行那些 6+ 代码来断言尝试要容易得多..catch 块。
Then again, maybe my 10 minute result of problem solving at friday evening just isn't the most intelligent way to do this.
再说一次,也许我在周五晚上解决问题的 10 分钟结果并不是最聪明的方法。
回答by Andreas Petersson
i did something very simple
我做了一些非常简单的事情
testBla(){
try {
someFailingMethod()
fail(); //method provided by junit
} catch(Exception e) {
//do nothing
}
}
回答by akuhn
Looking at the proposed answers, you can really feel the pain of not having closures in Java. IMHO, the most readable solution is ye good old try catch.
查看建议的答案,您真的可以感受到在 Java 中没有闭包的痛苦。恕我直言,最易读的解决方案是你很好的尝试捕捉。
@Test
public void test() {
...
...
try {
...
fail("No exception caught :(");
}
catch (RuntimeException ex) {
assertEquals(Whatever.class, ex.getCause().getClass());
assertEquals("Message", ex.getMessage());
}
}
回答by Jaime Hablutzel
I made a helper similar to the other posted ones:
我做了一个类似于其他发布的助手:
public class ExpectExceptionsExecutor {
private ExpectExceptionsExecutor() {
}
public static void execute(ExpectExceptionsTemplate e) {
Class<? extends Throwable> aClass = e.getExpectedException();
try {
Method method = ExpectExceptionsTemplate.class.getMethod("doInttemplate");
method.invoke(e);
} catch (NoSuchMethodException e1) {
throw new RuntimeException();
} catch (InvocationTargetException e1) {
Throwable throwable = e1.getTargetException();
if (!aClass.isAssignableFrom(throwable.getClass())) {
// assert false
fail("Exception isn't the one expected");
} else {
assertTrue("Exception captured ", true);
return;
}
;
} catch (IllegalAccessException e1) {
throw new RuntimeException();
}
fail("No exception has been thrown");
}
}
And the template the client should implement
以及客户端应该实现的模板
public interface ExpectExceptionsTemplate<T extends Throwable> {
/**
* Specify the type of exception that doInttemplate is expected to throw
* @return
*/
Class<T> getExpectedException();
/**
* Execute risky code inside this method
* TODO specify expected exception using an annotation
*/
public void doInttemplate();
}
And the client code would be something like this:
客户端代码将是这样的:
@Test
public void myTest() throws Exception {
ExpectExceptionsExecutor.execute(new ExpectExceptionsTemplate() {
@Override
public Class getExpectedException() {
return IllegalArgumentException.class;
}
@Override
public void doInttemplate() {
riskyMethod.doSomething(null);
}
});
}
It looks really verbose but if you use an IDE with good autocompletion you will only need to write the type of exception and the actual code under test. (the rest will be done by the IDE :D)
它看起来非常冗长,但如果您使用具有良好自动完成功能的 IDE,您只需要编写异常类型和实际测试代码。(其余的将由 IDE 完成:D)
回答by rwitzel
@akuhn:
@阿库恩:
Even without closures we can get a more readable solution (using catch-exception):
即使没有闭包,我们也可以获得更具可读性的解决方案(使用catch-exception):
import static com.googlecode.catchexception.CatchException.*;
public void test() {
...
...
catchException(nastyBoy).doNastyStuff();
assertTrue(caughtException() instanceof WhateverException);
assertEquals("Message", caughtException().getMessage());
}
回答by Marcus K.
For JUnit 5it is much easier:
对于JUnit 5,它更容易:
@Test
void testAppleIsSweetAndRed() throws Exception {
IllegalArgumentException ex = assertThrows(
IllegalArgumentException.class,
() -> testClass.appleIsSweetAndRed("orange", "red", "sweet"));
assertEquals("this is the exception message", ex.getMessage());
assertEquals(NullPointerException.class, ex.getCause().getClass());
}
By returning the exception object itself, assertThrows()
allows you to test every aspect regarding your thrown exceptions.
通过返回异常对象本身,assertThrows()
允许您测试有关抛出异常的各个方面。