為什么要測試異常流? 就像所有代碼一樣,測試覆蓋率會在代碼和應該生成的業務功能之間寫一個合同,從而為您提供代碼的有效文檔 ,以及增加的盡早且經常強調功能的功能。 我不會介紹測試的許多好處,而是只關注異常測試。
有很多方法可以測試從一段代碼引發的異常流。 假設您有一個受保護的方法,該方法要求參數不為null。 您將如何測試該狀況? 引發異常時,如何防止JUnit報告失敗? 該博客涵蓋了幾種不同的方法使用JUnit的的ExpectedException使用JUnit的@rule功能實現的高潮。
“舊”方式
在不久的將來,測試異常的過程需要大量的樣板代碼,您可以在其中啟動try / catch塊,如果代碼未產生預期的行為,則報告失敗,然后捕獲異常以查找異常。具體類型。 這是一個例子:
public class MyObjTest {@Testpublic void getNameWithNullValue() {try {MyObj obj = new MyObj();myObj.setName(null);fail('This should have thrown an exception');} catch (IllegalArgumentException e) {assertThat(e.getMessage().equals('Name must not be null'));}}
}
從這個舊示例中可以看到,測試用例中的許多行只是為了支持缺少專門測試異常處理的功能。 使用try / catch方法的一個好處是可以測試特定消息和預期異常上的任何自定義字段 。 我們將通過JUnit的ExpectedException和@Rule注釋進一步探討這一點。
JUnit添加了預期的異常
JUnit通過添加@Test注釋字段“ expected”來響應用戶對異常處理的需求。 目的是,如果引發的異常類型與注釋中存在的異常類匹配,則整個測試用例將通過。
public class MyObjTest {@Test(expected = IllegalArgumentException.class)public void getNameWithNullValue() {MyObj obj = new MyObj();myObj.setName(null);}
}
從較新的示例中可以看到,樣板代碼要少得多,并且測試非常簡潔,但是存在一些缺陷 。 主要缺陷是測試條件太寬泛。 假設簽名中有兩個變量,并且兩個變量都不能為null,那么如何知道為IllegalArgumentException拋出了哪個變量呢? 當您擴展了Throwable并需要檢查字段的存在時會發生什么? 在繼續閱讀時,請記住這些,隨后將有解決方案。
JUnit @Rule和ExpectedException
如果查看前面的示例,可能會看到期望拋出IllegalArgumentException,但是如果您有一個自定義異常,該怎么辦? 如果要確保該消息包含特定的錯誤代碼或消息怎么辦? 這是JUnit真正出色的地方,它提供了專門為異常測試量身定制的JUnit @Rule對象。 如果您不熟悉JUnit @Rule,請在此處閱讀文檔 。
ExpectedException
JUnit提供了一個JUnit類ExpectedException,該類旨在用作@Rule。 ExpectedException允許您的測試聲明預期會出現異常,并為您提供一些基本的內置功能來清楚地表達預期的行為。 與@Test(expected)批注功能不同,ExpectedException類允許您通過Hamcrest匹配器庫測試特定的錯誤消息和自定義字段。
JUnit的ExpectedException的示例
import org.junit.rules.ExpectedException;public class MyObjTest {@Rulepublic ExpectedException thrown = ExpectedException.none();@Testpublic void getNameWithNullValue() {thrown.expect(IllegalArgumentException.class);thrown.expectMessage('Name must not be null');MyObj obj = new MyObj();obj.setName(null);}
}
如前所述,該框架允許您測試特定消息,以確保在測試專門尋找的情況下拋出異常。 當懷疑多個參數的可空性時,這將非常有用。
自定義字段
可以說,ExpectedException框架最有用的功能是能夠使用Hamcrest匹配器測試您的自定義/擴展異常。 例如,您有一個自定義/擴展的異常將被拋出到一個方法中,并且該異常內部有一個“ errorCode”。 如何在不從上面列出的try / catch塊中引入樣板代碼的情況下測試該功能? 定制匹配器怎么樣!
可以從以下網址獲得此代碼: https : //github.com/mike-ensor/custom-exception-testing
解決方案:首先是測試用例
import org.junit.rules.ExpectedException;public class MyObjTest {@Rulepublic ExpectedException thrown = ExpectedException.none();@Testpublic void someMethodThatThrowsCustomException() {thrown.expect(CustomException.class);thrown.expect(CustomMatcher.hasCode('110501'));MyObj obj = new MyObj();obj.methodThatThrowsCustomException();}
}
解決方案:自定義匹配器
import com.thepixlounge.exceptions.CustomException;
import org.hamcrest.Description;
import org.hamcrest.TypeSafeMatcher;public class CustomMatcher extends TypeSafeMatcher<CustomException> {public static BusinessMatcher hasCode(String item) {return new BusinessMatcher(item);}private String foundErrorCode;private final String expectedErrorCode;private CustomMatcher(String expectedErrorCode) {this.expectedErrorCode = expectedErrorCode;}@Overrideprotected boolean matchesSafely(final CustomException exception) {foundErrorCode = exception.getErrorCode();return foundErrorCode.equalsIgnoreCase(expectedErrorCode);}@Overridepublic void describeTo(Description description) {description.appendValue(foundErrorCode).appendText(' was not found instead of ').appendValue(expectedErrorCode);}
}
注意:請訪問https://github.com/mike-ensor/custom-exception-testing以獲取可用的Hamcrest Matcher,JUnit @Rule和ExpectedException的副本。
在那里,您可以快速概覽一下測試代碼引發的異常的不同方法,以及從自定義異常類中測試特定消息和字段的能力。 請具體說明您的測試用例,并嘗試針對您為測試設置的確切用例,請記住,測試可以避免引入副作用漏洞!
祝您編程愉快,別忘了分享!
參考:在Mike的站點博客上,從JCG合作伙伴 Mike 那里 ,使用JUnit的ExpectedException和@Rule測試自定義異常 。
翻譯自: https://www.javacodegeeks.com/2012/10/testing-custom-exceptions-with-junits.html