1.寫在前面
基于junit 4.12版本,對junit源碼閱讀之后的理解和總結,如有不正確的地方,請多指正
2.junit的模塊
根據自己對源碼的理解,junit大體可以劃分為以下幾個模塊
1.Request負責發送測試指令
2.Runner負責運行測試用例
3.RunnerBuilder負責創建Runner
4.所有編寫的測試用例都被描述為一個TestClass,Runner運行時是通過解析TestClass來進行
5.Runner只是運行測試的一個入口,真正運行測試用例的實際上是Statement,Statement執行的時候會回調Runner子類中的一些具體方法
6.在Statement執行時,會過各種Rule
7.Statement執行完成后會產生一個執行結果Result
8.Notifier負責在測試用例運行期間的各種通知
接下對,結合源碼,對各個模塊進行分析
2.從JunitCore開始
junit是從JunitCore的main函數開始的
public static void main(String... args) {
//創建JunitCore實例,并且運行runaMain方法
//RealSystem只是封裝了打印輸出流,不影響主流程,暫時忽略
Result result = new JUnitCore().runMain(new RealSystem(), args);
//測試運行成功時正常退出,失敗時非正常退出
System.exit(result.wasSuccessful() ? 0 : 1);
}
接下來看runMain方法
Result runMain(JUnitSystem system, String... args) {
system.out().println("JUnit version " + Version.id());
//解析參數參數并獲得解析結果
JUnitCommandLineParseResult jUnitCommandLineParseResult = JUnitCommandLineParseResult.parse(args);
//創建并添一個監聽器
RunListener listener = new TextListener(system);
addListener(listener);
//創建請求,運行并返回運行結果
return run(jUnitCommandLineParseResult.createRequest(defaultComputer()));
}
先忽略細枝末節,先來看下最主要的run方法做了什么事
public Result run(Request request) {
//從Request中獲取Runner并調用run方法
return run(request.getRunner());
}
繼續看run方法
public Result run(Runner runner) {
//構建運行結果
Result result = new Result();
//創建并添加運行結果的監聽
RunListener listener = result.createListener();
notifier.addFirstListener(listener);
try {
//通知監聽器開始運行測試
notifier.fireTestRunStarted(runner.getDescription());
//開始運行
runner.run(notifier);
//通知監聽器測試運行結束
notifier.fireTestRunFinished(result);
} finally {
//移除監聽器
removeListener(listener);
}
return result;
}
先忽略監聽,首先讓我們重點來關注是測試是怎么運行的。
進入runner.run
方法可見,調用的是Runner
的抽象run
方法,可見真正的執行是由具體的實現來執行的
從以上流程中不難發現這個Runner
是從Request
中獲取的,那讓我們回過頭來看一下Request
的蹊蹺
3.Request的創建
讓我們回到runMain方法中最后一行return run(jUnitCommandLineParseResult.createRequest(defaultComputer()));
由此可見Request
是通過JUnitCommandLineParseResult
來創建見的,我們先來看一下入參Computer
,是通過defaultComputer()
方法獲取的,這個方法很簡單,只是return new Computer()
,看類的注釋Represents a strategy for computing runners and suites.
,字面意思好像是說表示一種計算runners
和suites
的策略,先不管了,繼續看createRequest
方法
public Request createRequest(Computer computer) {
//參數解析失敗的集合如果為空進入if塊
//parserErrors是runMain方法中JUnitCommandLineParseResult. parse方法獲得
if (parserErrors.isEmpty()) {
//創建請求
Request request = Request.classes(
computer, classes.toArray(new Class<?>[classes.size()]));
//對創建的默認的請求添加過濾功能
return applyFilterSpecs(request);
} else {
//如果參數解析出錯,導出錯誤
return errorReport(new InitializationError(parserErrors));
}
}
進入Request.classes()
方法繼續向下看
/**
* Create a <code>Request</code> that, when processed, will run all the tests
* in a set of classes.
*
* @param computer Helps construct Runners from classes
* @param classes the classes containing the tests
* @return a <code>Request</code> that will cause all tests in the classes to be run
*/
public static Request classes(Computer computer, Class<?>... classes) {
try {
//創建所有默認的可能的RunnerBuilder
AllDefaultPossibilitiesBuilder builder = new AllDefaultPossibilitiesBuilder(true);
//獲取?Suite,Suite是Runner的具體實現
Runner suite = computer.getSuite(builder, classes);
//調用runner方法并返回
return runner(suite);
} catch (InitializationError e) {
throw new RuntimeException(
"Bug in saff's brain: Suite constructor, called as above, should always complete");
}
}
看來這Request
是從runner
方法中創建的,繼續看runner
方法
public static Request runner(final Runner runner) {
return new Request() {
@Override
public Runner getRunner() {
return runner;
}
};
}
Request
的創建很簡單,直接new
了一個匿名的Request
并實現了getRunner()
的抽象方法
至此,可以看到runMain
方法中終于調用的Runner
是哪來的了
就是從這個匿名的Request
中實現的getRunner
方法中獲取的,而這個Runner
是由Request.classes
方法中創建的,而這個Runner
正是由Computer
這個類獲取的Suite
以上是默認的Request
方法的創建的Request
中Runner
的獲取,讓我們回到createRequest
方法中繼續向下看applyFilterSpecs
方法
private Request applyFilterSpecs(Request request) {
try {
for (String filterSpec : filterSpecs) {
//根據參數解析的filterSpecs創建過濾器
Filter filter = FilterFactories.createFilterFromFilterSpec(
request, filterSpec);
request = request.filterWith(filter);
}
return request;
} catch (FilterNotCreatedException e) {
return errorReport(e);
}
}
繼續看request.filterWith
public Request filterWith(Filter filter) {
return new FilterRequest(this, filter);
}
直接new
了一個FilterRequest
,此處是采用裝飾者模式,對原有的Request
進行加工,增加了過濾功能,不再贅述
因此可以看出,JunitCore
中的run
方法
public Result run(Request request) {
return run(request.getRunner());
}
是從Request
實例中(如果設置了過濾器,則是FilterRequest
實例)調用getRunner()
方法,獲取Suite
實例,真正運行的是Suite
中的run()
方法
(FilterRequest
中的getRunner
方法會過濾掉實現了Filterable
并滿足過濾條件的Runner
)
下面讓我們來總結一下這個調用的時序