JUnit源碼解析
發表于:2016-10-04來源:saymagic作者:saymagic點擊數:
標簽:junit
JUnit源碼解析JUnit是由 Erich Gamma 和 Kent Beck 編寫的一個回歸測試框架,以Eclipse、IDEA等為代表的Java開發環境都對JUnit提供了非常友善的支持。提到Erich Gamma,他就是大名鼎鼎的《設計模式
JUnit源碼解析
JUnit是由 Erich Gamma 和 Kent Beck 編寫的一個
回歸測試框架,以Eclipse、IDEA等為代表的
Java開發環境都對JUnit提供了非常友善的支持。提到Erich Gamma,他就是大名鼎鼎的《設計模式:可復用面向對象軟件的基礎》一書的作者之一。因此,JUnit當中的設計模式的運用相當得當,所以,JUnit的源碼可謂相當優良的一本武林秘籍,非常值得一看。 本文基于JUnit4.12,將從JUnit的運行流程,Match驗證,兩個方面,來對JUnit的源碼進行整體的分析。
運行流程
JUnit的啟動方式有很多,比如在Android Studio中我們可以直接點擊某個被@Test注解的函數來運行:
此時,啟動的是JUniteStarter,該類是intellij為我們提供的。感興趣可以查看其源碼: https://github.com/JetBrains/intellij-community/blob/master/plugins/junit_rt/src/com/intellij/rt/execution/junit/JUnitStarter.
java
如果我們使用gradle, 可以執行gradle test運行測試,實際上是在一個線程中執行SuiteTestClassProcessor的processTestClass方法來進行啟動。其源碼可以查看https://github.com/gradle/gradle/blob/master/subprojects/testing-base/src/main/java/org/gradle/api/internal/tasks/testing/SuiteTestClassProcessor.java
如上兩種都是第三方工具為我們提供的便捷方式,實際上JUnit也提供了一個名為JUnitCore的類來供我們方便的運行
測試用例。
盡管啟動JUnit的方式有很多,但這都是打開與JUnit對話的一些方式,最終執行的還是JUnit當中的起到核心作用的一些類,為了讓大家對這些核心boss有一個初步了解,我畫了一個類圖:
上圖中僅是JUnit中的幾個核心的類,也是本分主要分析的對象。這里先給出一些對象的職責,可以有個大體的了解,后面會通過代碼就會更清楚每個對象是如何完成這些職責的:
在類圖的中央,有個叫做ParentRunne的對象很引人注目,它繼承自Runner.
Runner則表示著JUnit對整個測試的抽象
Runner實現了Describable接口,Describable接口中唯一的函數getDescription()返回了Description對象,記錄著測試的信息。
Statement 是一個抽象類,其 evaluate()函數代表著在測試中將被執行的方法。
ParentRunner 共有兩個子類,BlockJUnit4ClassRunner 用來運行單個測試類,Suite用來一起運行多個測試類
RunnerBuilder 是生產Runner的策略,如使用@RunWith(Suite.class)標注的類需要使用Suite, 被@Ignore標注的類需要使用IgnoreClassRunner。
TestClass是對被測試的類的封裝
綜上,我們先從ParentRunner看起,其構造函數如下:
protected ParentRunner(Class<?> testClass) throws InitializationError {
this.testClass = createTestClass(testClass);
validate();
}
this.testClass即前文所說的TestClass,我們進入createTestClass方法來查看其如何將class對象轉換為TestClass。
protected TestClass createTestClass(Class<?> testClass) {
return new TestClass(testClass);
}
并沒什么東西,具體的邏輯都寫在TestClass的內部:
public TestClass(Class<?> clazz) {
this.clazz = clazz;
if (clazz != null && clazz.getConstructors().length > 1) {
throw new IllegalArgumentException(
"Test class can only have one constructor");
}
Map<Class<? extends Annotation>, List<FrameworkMethod>> methodsForAnnotations =
new LinkedHashMap<Class<? extends Annotation>, List<FrameworkMethod>>();
Map<Class<? extends Annotation>, List<FrameworkField>> fieldsForAnnotations =
new LinkedHashMap<Class<? extends Annotation>, List<FrameworkField>>();
scanAnnotatedMembers(methodsForAnnotations, fieldsForAnnotations);
this.methodsForAnnotations = makeDeeplyUnmodifiable(methodsForAnnotations);