Junit源碼閱讀(一)


Junit的工程結構

Junit工程結構

從上圖能夠清楚的看出Junit大體分爲幾個版塊,接下來一一簡略介紹這些版塊的做用。java

  • runner:定義了Junit模型中的許多基本概念,只要是一些虛類和接口,是整個Junit工程的基石git

  • runners:提供了從註解中使用反射完成測試用例執行的實現github

  • interval:提供了在Runner中許多虛類的默認實現,包括各種RunnerBuilder如基於註解提供Builder、各種Matchers、各種Request如類測試Request、方法測試Request。安全

  • rules:用於用戶自定義擴展,規定對於不一樣statement的用戶自定義行爲app

  • matchers:主要用於AssertThat方法,在4.4以後廢棄框架

  • validator:從類、方法和域多個方面檢查測試樣例ide

  • experimental:一些測試特性測試

Runner中的基本概念

  • Runner:以Notifier爲參數容許測試樣例,運行過程當中的Notifier負責監視測試過程ui

  • Request:負責記錄測試樣例的Description信息,同事負責提供對應的Runner。能夠經過Computer結合指定或默認的RunnerBuilder來直接爲一系列測試類統一提供Runnerthis

  • Description:描述測試樣例,使用Composite模式,組合多個樣例

  • Result:記錄異常和失敗,內置一個Listener來實現與測試過程的同步,測試完成時count自增,有樣例失敗則加入Failure列表

  • Failure:將斷言失敗和拋出異常綜合在同一個框架下,同時提供了Description的信息

  • Listener:監視測試過程,典型的觀察者模式

  • Notifier:管理一系列Listener,保證線程安全

  • Filter:指定條件,只運行符合條件的測試樣例,能夠動態添加,爲每次測試增長了靈活性

Runner中的依賴關係

Runner中的依賴示意圖

JunitCore負責提供給用戶統一的交互,從命令行運行測試樣例。Notifier是一個虛類,子類須要實現如何通知Listener的方法,負責管理Listener集合,內部內置了一個靜態的SafeNotifier,該類提供了一個run方法,來簡單依次通知全部Listener,它用來實如今測試開始和失敗出現的時候通知全部Listener。

Description是對測試樣例的建模,用來組合多個測試樣例,是Runner中的核心內容。

Filter也是一個虛類,子類應該實現shouldRun方法來決定對於Description是否運行。同時依然實現一個靜態方法來提供什麼都不過濾,以及一個判斷原子描述是否等於指望描述的過濾器,對於非原子描述若其子描述均不等於指望描述則濾掉。以下列代碼所示:

/**
     * Returns a {@code Filter} that only runs the single method described by
     * {@code desiredDescription}
     */
    public static Filter matchMethodDescription(final Description desiredDescription) {
        return new Filter() {
            @Override
            public boolean shouldRun(Description description) {
                if (description.isTest()) {
                    return desiredDescription.equals(description);
                }

                // explicitly check if any children want to run
                for (Description each : description.getChildren()) {
                    if (shouldRun(each)) {
                        return true;
                    }
                }
                return false;
            }

            @Override
            public String describe() {
                return String.format("Method %s", desiredDescription.getDisplayName());
            }
        };
    }

Failure組合了Description和Throwable,爲運行時異常和斷言錯誤屏蔽了不一致的方面,能夠向上提供錯誤信息和樣例信息。

Request負責提供具體的Runner來run對應的測試樣例,同時是Filter做用的主體,對於Filter,會返回一個新的FilterRequest,代碼以下:

package org.junit.internal.requests;

import org.junit.internal.runners.ErrorReportingRunner;
import org.junit.runner.Request;
import org.junit.runner.Runner;
import org.junit.runner.manipulation.Filter;
import org.junit.runner.manipulation.NoTestsRemainException;

/**
 * A filtered {@link Request}.
 */
public final class FilterRequest extends Request {
    private final Request request;
    /*
     * We have to use the f prefix, because IntelliJ's JUnit4IdeaTestRunner uses
     * reflection to access this field. See
     * https://github.com/junit-team/junit/issues/960
     */
    private final Filter fFilter;

    /**
     * Creates a filtered Request
     *
     * @param request a {@link Request} describing your Tests
     * @param filter {@link Filter} to apply to the Tests described in
     * <code>request</code>
     */
    public FilterRequest(Request request, Filter filter) {
        this.request = request;
        this.fFilter = filter;
    }

    @Override
    public Runner getRunner() {
        try {
            Runner runner = request.getRunner();
            fFilter.apply(runner);
            return runner;
        } catch (NoTestsRemainException e) {
            return new ErrorReportingRunner(Filter.class, new Exception(String
                    .format("No tests found matching %s from %s", fFilter
                            .describe(), request.toString())));
        }
    }
}

Runner中的JunitCore

JunitCore運行時序圖

JUnitCore使用外觀模式(facade),對外提供一致的界面,同時支持運行JUnit 4或JUnit 3.8.x用例,經過命令行執行用例.首先它使用jUnitCommandLineParseResult解析外部參數,將默認的TextListener加入內置的Notifier。它所運行的Runner也是由jUnitCommandLineParseResult提供的,先行經過測試filter掉不須要的樣例,最後調用Runner的run方法。

相關文章
相關標籤/搜索