有一段時間,人們會希望并期望JUnit @Test案例失敗。 盡管這種情況很少見,但確實發生了。 我需要檢測JUnit測試何時失敗,然后(如果期望的話)通過而不是失敗。 具體情況是我正在測試一段代碼,該代碼可能會在對象調用內引發Assert錯誤。 該代碼被編寫為對流行的新Fest Assertions框架的增強,因此,為了測試功能,人們可能希望測試用例會故意失敗。
一個解法
一種可能的解決方案是將JUnit @Rule提供的功能與注釋形式的自定義標記一起使用。
為什么要使用@Rule?
@Rule對象為測試類和每個測試用例提供了類似于AOP的接口。 在運行每個測試用例之前,將重置規則,并且它們以@Around AspectJ建議的樣式公開測試用例的工作方式。
必需的代碼元素
- @Rule對象檢查每個@Test用例的狀態
- @ExpectedFailure自定義標記注釋
- 測試用例證明代碼有效!
- 如果帶注釋的測試用例沒有失敗,則拋出可選的特定異常
注意:工作代碼在我的github頁面上可用,并已添加到Maven Central。 隨意分叉項目并提交拉取請求 Maven用法
<dependency><groupId>com.clickconcepts.junit</groupId><artifactId>expected-failure</artifactId><version>0.0.9</version>
</dependency>
用法示例
在此示例中,“ exception”對象是Fest斷言增強的ExpectedException(請查看我的下一篇文章以展示此功能)。 預期的異常將產生斷言,并且為了測試這些斷言,必須將測試用例標記為@ExpectedFailure
public class ExceptionAssertTest {@Rulepublic ExpectedException exception = ExpectedException.none();@Rulepublic ExpectedTestFailureWatcher watcher = ExpectedTestFailureWatcher.instance();@Test@ExpectedFailure('The matcher should fail becasue exception is not a SimpleException')public void assertSimpleExceptionAssert_exceptionIsOfType() {// expected exception will be of type 'SimpleException'exception.instanceOf(SimpleException.class);// throw something other than SimpleException...expect failurethrow new RuntimeException('this is an exception');}
}
提醒一下,最新代碼可在我的github頁面上找到 。
@規則代碼(ExpectedTestFailureWatcher.java)
import org.junit.rules.TestRule;
import org.junit.runner.Description;
import org.junit.runners.model.Statement;
// YEAH Guava!!
import static com.google.common.base.Strings.isNullOrEmpty;public class ExpectedTestFailureWatcher implements TestRule {/*** Static factory to an instance of this watcher** @return New instance of this watcher*/public static ExpectedTestFailureWatcher instance() {return new ExpectedTestFailureWatcher();}@Overridepublic Statement apply(final Statement base, final Description description) {return new Statement() {@Overridepublic void evaluate() throws Throwable {boolean expectedToFail = description.getAnnotation(ExpectedFailure.class) != null;boolean failed = false;try {// allow test case to executebase.evaluate();} catch (Throwable exception) {failed = true;if (!expectedToFail) {throw exception; // did not expect to fail and failed...fail}}// placed outside of catchif (expectedToFail && !failed) {throw new ExpectedTestFailureException(getUnFulfilledFailedMessage(description));}}/*** Extracts detailed message about why test failed* @param description* @return*/private String getUnFulfilledFailedMessage(Description description) {String reason = null;if (description.getAnnotation(ExpectedFailure.class) != null) {reason = description.getAnnotation(ExpectedFailure.class).reason();}if (isNullOrEmpty(reason)) {reason = 'Should have failed but didn't';}return reason;}};}
}
@ExpectedFailure定制注釋(ExpectedFailure.java)
import java.lang.annotation.*;/*** Initially this is just a marker annotation to be used by a JUnit4 Test case in conjunction* with ExpectedTestFailure @Rule to indicate that a test is supposed to be failing*/
@Documented
@Retention(RetentionPolicy.RUNTIME)
@Target(value = ElementType.METHOD)
public @interface ExpectedFailure {// TODO: enhance by adding specific information about what type of failure expected//ClassassertType() default Throwable.class;/*** Text based reason for marking test as ExpectedFailure* @return String*/String reason() default '';
}
自定義異常(可選,您可以輕松地拋出RuntimeException或現有的自定義異常)
public class ExpectedTestFailureException extends Throwable {public ExpectedTestFailureException(String message) {super(message);}
}
一個人不能利用預期的故障標記能力嗎?
強大的功能伴隨著巨大的責任 ,建議您如果不完全了解測試失敗的原因,請不要將測試標記為@ExpectedFailure。 建議謹慎執行此測試方法。 請勿使用@ExpectedFailure注釋替代@Ignore
將來可能的增強可能包括指定在測試用例執行期間確定的特定斷言或特定消息的方法。
已知的問題
在此當前狀態下,@ ExpectedFailure批注可以掩蓋其他聲明,并且在將來進行增強之前,建議明智地使用此方法。
參考:在Mike的站點博客上,允許JUnit測試通過我們的JCG合作伙伴 Mike的失敗測試案例 。
翻譯自: https://www.javacodegeeks.com/2012/09/junit-pass-test-case-on-failures.html