在爲Java API編寫單元測試時,可能會出現您想要執行更多詳細的異常驗證的情況。即比由JUnit提供的@test註釋提供的更多。在Java中,我如何使用JUnit驗證拋出的異常?
例如,考慮一個類應該從其他接口捕獲一個異常,包裝該異常並拋出包裝的異常。您可能需要驗證:
- 引發包裝異常的確切方法調用。
- 包裝異常具有原始異常作爲其原因。
- 包裝異常的消息。
這裏主要的一點是,你要在一個單元測試(而不是有關是否應該驗證像異常信息的事情辯論)異常的PERF的額外的驗證。
這是什麼方法?
在爲Java API編寫單元測試時,可能會出現您想要執行更多詳細的異常驗證的情況。即比由JUnit提供的@test註釋提供的更多。在Java中,我如何使用JUnit驗證拋出的異常?
例如,考慮一個類應該從其他接口捕獲一個異常,包裝該異常並拋出包裝的異常。您可能需要驗證:
這裏主要的一點是,你要在一個單元測試(而不是有關是否應該驗證像異常信息的事情辯論)異常的PERF的額外的驗證。
這是什麼方法?
在JUnit 4可以使用ExpectedException規則很容易完成。
這裏是例如從的javadoc:
// 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?");
}
}
以下輔助方法(改編自this博客文章)的伎倆:
/**
* 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());
}
測試代碼然後可以調用此如下:
TestHelper.expectException(
new Runnable() {
public void run() {
classInstanceBeingTested.methodThatThrows();
}
},
WrapperException.class,
"Exception Message",
causeException
);
有什麼辦法可以壓縮Java中的內聯類/方法嗎? – Iain 2009-04-24 14:19:07
如your answer提供,這是一個好的方法。除此之外:
您可以將函數expectException
包裝爲一個新的註釋,稱爲ExpectedException
。
帶註釋的方法是這樣的:
@Test
@ExpectedException(class=WrapperException.class, message="Exception Message", causeException)
public void testAnExceptionWrappingFunction() {
//whatever you test
}
這種方式會更可讀的,但它是完全一樣的方法。
另一個原因是:我喜歡註解:)
針對JUnit 3.x的
public void test(){
boolean thrown = false;
try{
mightThrowEx();
} catch (Surprise expected){
thrown = true;
assertEquals("message", expected.getMessage());
}
assertTrue(thrown);
}
甜....... !!! – Rachel 2013-06-19 20:28:05
直到這個職位,我已經做這做了我的異常驗證:
try {
myObject.doThings();
fail("Should've thrown SomeException!");
} catch (SomeException e) {
assertEquals("something", e.getSomething());
}
我花了一些時間思考問題,並提出了以下內容(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);
看着這兩個我不能決定我更喜歡哪一個。我想這是實現目標(在我的情況下,具有functor參數的斷言方法)從長遠來看不值得的目標之一,因爲執行這些6+代碼來斷言嘗試..catch塊。
然後,也許我在週五晚上10分鐘解決問題的結果並不是最聰明的方法。
我做了很簡單的
testBla(){
try {
someFailingMethod()
fail(); //method provided by junit
} catch(Exception e) {
//do nothing
}
}
望着提出的答案,你可以真正感受到在Java中沒有倒閉之痛。恕我直言,最可讀的解決方案是你好老嘗試趕上。
@Test
public void test() {
...
...
try {
...
fail("No exception caught :(");
}
catch (RuntimeException ex) {
assertEquals(Whatever.class, ex.getCause().getClass());
assertEquals("Message", ex.getMessage());
}
}
我提出了類似的其他發佈者的輔助:
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");
}
}
而且模板中的客戶端應該實現
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();
}
而且客戶端代碼會是這樣的:
@Test
public void myTest() throws Exception {
ExpectExceptionsExecutor.execute(new ExpectExceptionsTemplate() {
@Override
public Class getExpectedException() {
return IllegalArgumentException.class;
}
@Override
public void doInttemplate() {
riskyMethod.doSomething(null);
}
});
}
它看起來非常冗長,但是如果您使用具有良好自動完成功能的IDE,則只需編寫異常類型和待測試的實際代碼。 (其餘部分將通過IDE來完成:d)
@akuhn:
即使沒有倒閉,我們可以(用catch-exception)得到更可讀的解決方案:
import static com.googlecode.catchexception.CatchException.*;
public void test() {
...
...
catchException(nastyBoy).doNastyStuff();
assertTrue(caughtException() instanceof WhateverException);
assertEquals("Message", caughtException().getMessage());
}
對於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());
}
通過返回的異常對象本身,assertThrows()
讓你測試對於每一個方面你拋出異常。
如果您能夠將最合適的答案作爲正確的答案,我將不勝感激。 – guerda 2009-10-22 11:32:07
你不應該關心*哪個*方法拋出異常:這是一個實現細節。 – Raedwald 2015-12-18 19:48:10