国产xxxx99真实实拍_久久不雅视频_高清韩国a级特黄毛片_嗯老师别我我受不了了小说

資訊專欄INFORMATION COLUMN

Junit源碼閱讀(二)之樣例運(yùn)行的機(jī)制

meteor199 / 3001人閱讀

摘要:前言在上次的博客中我們提到了最終由以為參數(shù)執(zhí)行測(cè)試樣例,但并沒(méi)有解釋到底測(cè)試方法是如何被運(yùn)行起來(lái)的,一些諸如之類的特性又到底是如何實(shí)現(xiàn)的呢。這次我們就集中深入的運(yùn)行機(jī)制來(lái)探究樣例是如何被運(yùn)行的。使用拿到的直接運(yùn)行方法。

前言

在上次的博客中我們提到了最終由Runner以Notifier為參數(shù)執(zhí)行測(cè)試樣例,但并沒(méi)有解釋到底測(cè)試方法是如何被運(yùn)行起來(lái)的,一些諸如RunWith、RunAfter之類的特性又到底是如何實(shí)現(xiàn)的呢。這次我們就集中深入Runner的運(yùn)行機(jī)制來(lái)探究樣例是如何被運(yùn)行的。

包裝注解信息——FrameWorkMember

首先我們需要把注解等用戶配置信息收集起來(lái)并attach到對(duì)應(yīng)的方法、類和屬性上,為了在之后的代碼中能夠方便的取到這些信息,我們要包裝原有的類、方法和域,分別如下。

TestClass

TestClass包含原有的clazz信息,并且維護(hù)了兩個(gè)Map來(lái)管理它所包含的方法與屬性,每個(gè)map的鍵是注解,而值是標(biāo)上注解的FrameWorkMethod或FrameWorkField。同時(shí)TestClass還默認(rèn)內(nèi)置兩個(gè)Comparator來(lái)排序自己所包含的方法和屬性。

下面給出如何構(gòu)造一個(gè)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, List> methodsForAnnotations =
                new LinkedHashMap, List>();
        Map, List> fieldsForAnnotations =
                new LinkedHashMap, List>();

        scanAnnotatedMembers(methodsForAnnotations, fieldsForAnnotations);

        this.methodsForAnnotations = makeDeeplyUnmodifiable(methodsForAnnotations);
        this.fieldsForAnnotations = makeDeeplyUnmodifiable(fieldsForAnnotations);
    }

    protected void scanAnnotatedMembers(Map, List> methodsForAnnotations, Map, List> fieldsForAnnotations) {
        for (Class eachClass : getSuperClasses(clazz)) {
            for (Method eachMethod : MethodSorter.getDeclaredMethods(eachClass)) {
                addToAnnotationLists(new FrameworkMethod(eachMethod), methodsForAnnotations);
            }
            // ensuring fields are sorted to make sure that entries are inserted
            // and read from fieldForAnnotations in a deterministic order
            for (Field eachField : getSortedDeclaredFields(eachClass)) {
                addToAnnotationLists(new FrameworkField(eachField), fieldsForAnnotations);
            }
        }
    }

TestClass的主要功能就是向Runner提供clazz信息以及附帶的注解信息,上文的addToAnnotationLists將對(duì)應(yīng)member加入該annotation映射的member列表。下面給一個(gè)TestClass的方法列表截圖,大家可以感受一下。

FrameWorkMethod

我們先給出它的父類FrameWorkMember的定義

public abstract class FrameworkMember> implements
        Annotatable {
    abstract boolean isShadowedBy(T otherMember);

    boolean isShadowedBy(List members) {
        for (T each : members) {
            if (isShadowedBy(each)) {
                return true;
            }
        }
        return false;
    }

    protected abstract int getModifiers();

    /**
     * Returns true if this member is static, false if not.
     */
    public boolean isStatic() {
        return Modifier.isStatic(getModifiers());
    }

    /**
     * Returns true if this member is public, false if not.
     */
    public boolean isPublic() {
        return Modifier.isPublic(getModifiers());
    }

    public abstract String getName();

    public abstract Class getType();

    public abstract Class getDeclaringClass();
}

FrameWorkMethod包裝了方法信息以及方法相關(guān)的注解以及一些基本的驗(yàn)證方法比如validatePublicVoid和是否被其他FrameWorkMethod覆蓋的判斷方法,除父類要求外它主要提供的信息如下:

Annotations

Method

ReturnType

ParameterTypes

FrameWorkField

同F(xiàn)rameWorkMethod差不多,F(xiàn)rameWorkField和它繼承自同一父類,較為簡(jiǎn)單,此處就不再詳細(xì)介紹了。

真正的執(zhí)行單元——Statement

Statement是最小的執(zhí)行單元,諸如RunAfter、RunWith等功能均是通過(guò)嵌套Statement來(lái)實(shí)現(xiàn)的,下面我們先給出Statement的定義,再給出一個(gè)嵌套的例子。

public abstract class Statement {
    /**
     * Run the action, throwing a {@code Throwable} if anything goes wrong.
     */
    public abstract void evaluate() throws Throwable;
}

下面以RunAfter的實(shí)現(xiàn)為例來(lái)說(shuō)明:

public class RunAfters extends Statement {
   private final Statement next;

   private final Object target;

   private final List afters;

   public RunAfters(Statement next, List afters, Object target) {
       this.next = next;
       this.afters = afters;
       this.target = target;
   }

   @Override
   public void evaluate() throws Throwable {
       List errors = new ArrayList();
       try {
           next.evaluate();
       } catch (Throwable e) {
           errors.add(e);
       } finally {
           for (FrameworkMethod each : afters) {
               try {
                   each.invokeExplosively(target);
               } catch (Throwable e) {
                   errors.add(e);
               }
           }
       }
       MultipleFailureException.assertEmpty(errors);
   }
}

可以看出新的Statement執(zhí)行時(shí)會(huì)先執(zhí)行舊有的Statement,再將附加上的一系列方法以target為參數(shù)運(yùn)行。

組合方法測(cè)試的Runner實(shí)現(xiàn)——BlockJunitClassRunner

Junit使用虛類ParentRunner來(lái)管理復(fù)合的Runner,使用composite模式,而B(niǎo)lockJunitClassRunner是ParentRunner的一個(gè)子類,主要負(fù)責(zé)同一測(cè)試類多個(gè)方法的組合測(cè)試,也就是最常用的情形。我們首先還是聚焦在如何運(yùn)行測(cè)試樣例上。

首先看ParentRunner如何實(shí)現(xiàn)run方法

@Override
    public void run(final RunNotifier notifier) {
        EachTestNotifier testNotifier = new EachTestNotifier(notifier,
                getDescription());
        try {
            Statement statement = classBlock(notifier);
            statement.evaluate();
        } catch (AssumptionViolatedException e) {
            testNotifier.addFailedAssumption(e);
        } catch (StoppedByUserException e) {
            throw e;
        } catch (Throwable e) {
            testNotifier.addFailure(e);
        }
    }

這里有個(gè)classBlock方法用來(lái)提供真正運(yùn)行的Statement,下面我們看一看

protected Statement classBlock(final RunNotifier notifier) {
        Statement statement = childrenInvoker(notifier);
        if (!areAllChildrenIgnored()) {
            statement = withBeforeClasses(statement);
            statement = withAfterClasses(statement);
            statement = withClassRules(statement);
        }
        return statement;
    }

這個(gè)過(guò)程就是先通過(guò)反射獲得初始Statement,然后附加上RunBefore、RunAfter、用戶自定義Rule,我們來(lái)看一下初始Statement是如何生成的。

其過(guò)程是先取得所有通過(guò)過(guò)濾器的Childeren,再使用內(nèi)置的調(diào)度器來(lái)分別按順序調(diào)用runChild方法,下面我們給出BlockJunit4ClassRunner的runChild方法

@Override
    protected void runChild(final FrameworkMethod method, RunNotifier notifier) {
        Description description = describeChild(method);
        if (isIgnored(method)) {
            notifier.fireTestIgnored(description);
        } else {
            Statement statement;
            try {
                statement = methodBlock(method);
            }
            catch (Throwable ex) {
                statement = new Fail(ex);
            }
            runLeaf(statement, description, notifier);
        }
    }

這里面最重要的就是RunLeaf也就是原子測(cè)試方法以及如何為單個(gè)方法生成的Statement——methodBlock,我們?cè)谙旅娣謩e給出。

protected final void runLeaf(Statement statement, Description description,
            RunNotifier notifier) {
        EachTestNotifier eachNotifier = new EachTestNotifier(notifier, description);
        eachNotifier.fireTestStarted();
        try {
            statement.evaluate();
        } catch (AssumptionViolatedException e) {
            eachNotifier.addFailedAssumption(e);
        } catch (Throwable e) {
            eachNotifier.addFailure(e);
        } finally {
            eachNotifier.fireTestFinished();
        }
    }

RunLeaf的邏輯并不難,先通知Notifier測(cè)試開(kāi)始,再直接調(diào)用statement的evaluate方法,最后通知Notifier測(cè)試結(jié)束。我們?cè)賮?lái)看看statement是如何生成的。

protected Statement methodBlock(final FrameworkMethod method) {
        Object test;
        try {
            test = new ReflectiveCallable() {
                @Override
                protected Object runReflectiveCall() throws Throwable {
                    return createTest(method);
                }
            }.run();
        } catch (Throwable e) {
            return new Fail(e);
        }

        Statement statement = methodInvoker(method, test);
        statement = possiblyExpectingExceptions(method, test, statement);
        statement = withPotentialTimeout(method, test, statement);
        statement = withBefores(method, test, statement);
        statement = withAfters(method, test, statement);
        statement = withRules(method, test, statement);
        return statement;
    }

上述代碼的邏輯還是比較復(fù)雜的,這里簡(jiǎn)單概述一下,首先構(gòu)造測(cè)試類的實(shí)例,然后為對(duì)應(yīng)method構(gòu)造statement的子類InvokeMethod,然后調(diào)用FrameWorkMethod的反射運(yùn)行方法,如下:

public class InvokeMethod extends Statement {
    private final FrameworkMethod testMethod;
    private final Object target;

    public InvokeMethod(FrameworkMethod testMethod, Object target) {
        this.testMethod = testMethod;
        this.target = target;
    }

    @Override
    public void evaluate() throws Throwable {
        testMethod.invokeExplosively(target);
    }
}
組合類測(cè)試的Runner實(shí)現(xiàn)——Suite

Suite是對(duì)于ParentRunner的另一子類實(shí)現(xiàn),主要用于多個(gè)測(cè)試類的情形。Suite自己維護(hù)一個(gè)runner列表,實(shí)現(xiàn)了getChilderen方法,其層次是在上文中提到的runChildren里,這一部分需要取出children節(jié)點(diǎn)然后調(diào)用runChild方法。我們著重考察suite和BlockJunit4ClassRunner在getChildren和runChild方法上的區(qū)別。Suite通過(guò)用戶傳入的runnerBuilder為每個(gè)類多帶帶建立runner作為children返回,而后者則返回帶Test注解的FrameWorkMethod列表。使用getChildren拿到的runner直接運(yùn)行run方法。下面我們給出RunnerBuilder是如何為一系列測(cè)試類提供一系列對(duì)應(yīng)的Runner,說(shuō)來(lái)也簡(jiǎn)單,就是使用為單個(gè)類建立Runner的方法為每個(gè)測(cè)試類建立最后組成一個(gè)集合。但是此處需要防止遞歸——this builder will throw an exception if it is requested for another runner for {@code parent} before this call completes(說(shuō)實(shí)話這段如何防止遞歸我也沒(méi)看懂,有看懂的兄弟求教)。對(duì)于Suite而言,一般就是它維護(hù)一個(gè)BlockJUnit4ClassRunner列表。

public abstract class RunnerBuilder {
    private final Set> parents = new HashSet>();

    /**
     * Override to calculate the correct runner for a test class at runtime.
     *
     * @param testClass class to be run
     * @return a Runner
     * @throws Throwable if a runner cannot be constructed
     */
    public abstract Runner runnerForClass(Class testClass) throws Throwable;

    /**
     * Always returns a runner, even if it is just one that prints an error instead of running tests.
     *
     * @param testClass class to be run
     * @return a Runner
     */
    public Runner safeRunnerForClass(Class testClass) {
        try {
            return runnerForClass(testClass);
        } catch (Throwable e) {
            return new ErrorReportingRunner(testClass, e);
        }
    }

    Class addParent(Class parent) throws InitializationError {
        if (!parents.add(parent)) {
            throw new InitializationError(String.format("class "%s" (possibly indirectly) contains itself as a SuiteClass", parent.getName()));
        }
        return parent;
    }

    void removeParent(Class klass) {
        parents.remove(klass);
    }

    /**
     * Constructs and returns a list of Runners, one for each child class in
     * {@code children}.  Care is taken to avoid infinite recursion:
     * this builder will throw an exception if it is requested for another
     * runner for {@code parent} before this call completes.
     */
    public List runners(Class parent, Class[] children)
            throws InitializationError {
        addParent(parent);

        try {
            return runners(children);
        } finally {
            removeParent(parent);
        }
    }

    public List runners(Class parent, List> children)
            throws InitializationError {
        return runners(parent, children.toArray(new Class[0]));
    }

    private List runners(Class[] children) {
        List runners = new ArrayList();
        for (Class each : children) {
            Runner childRunner = safeRunnerForClass(each);
            if (childRunner != null) {
                runners.add(childRunner);
            }
        }
        return runners;
    }
}
在注解中加上參數(shù) BlockJUnit4ClassRunnerWithParameters

Junit使用BlockJUnit4ClassRunnerWithParameters繼承BlockJUnit4ClassRunner來(lái)完成對(duì)于組合方法的帶參數(shù)測(cè)試。它覆寫(xiě)了createTest方法和對(duì)構(gòu)造器和域的驗(yàn)證方法。

@Override
    public Object createTest() throws Exception {
        InjectionType injectionType = getInjectionType();
        switch (injectionType) {
            case CONSTRUCTOR:
                return createTestUsingConstructorInjection();
            case FIELD:
                return createTestUsingFieldInjection();
            default:
                throw new IllegalStateException("The injection type "
                        + injectionType + " is not supported.");
        }
    }
Parameterized

Parameterized繼承了Suite,用來(lái)完成對(duì)多個(gè)類的組合測(cè)試的帶參數(shù)版本。它提供三大注解——Parameters、Parameter、UseParametersRunnerFactory,前兩者是用來(lái)指定參數(shù)的,后者用來(lái)指定對(duì)于每一個(gè)測(cè)試類如何生成Runner的工廠,默認(rèn)工廠返回BlockJUnit4ClassRunnerWithParameters。我們下面給出內(nèi)置的工廠類如何創(chuàng)建runner的代碼。

        private List createRunnersForParameters(
                Iterable allParameters, String namePattern,
                ParametersRunnerFactory runnerFactory) throws Exception {
            try {
                List tests = createTestsForParameters(
                        allParameters, namePattern);
                List runners = new ArrayList();
                for (TestWithParameters test : tests) {
                    runners.add(runnerFactory
                            .createRunnerForTestWithParameters(test));
                }
                return runners;
            } catch (ClassCastException e) {
                throw parametersMethodReturnedWrongType();
            }
        }

        private List createRunners() throws Throwable {
            Parameters parameters = getParametersMethod().getAnnotation(
                    Parameters.class);
            return Collections.unmodifiableList(createRunnersForParameters(
                    allParameters(), parameters.name(),
                    getParametersRunnerFactory()));
        }           
               
                                           
                       
                 
            
                     
             
               

文章版權(quán)歸作者所有,未經(jīng)允許請(qǐng)勿轉(zhuǎn)載,若此文章存在違規(guī)行為,您可以聯(lián)系管理員刪除。

轉(zhuǎn)載請(qǐng)注明本文地址:http://specialneedsforspecialkids.com/yun/65452.html

相關(guān)文章

  • Junit源碼閱讀(一)

    摘要:是對(duì)測(cè)試樣例的建模,用來(lái)組合多個(gè)測(cè)試樣例,是中的核心內(nèi)容。也是一個(gè)虛類,子類應(yīng)該實(shí)現(xiàn)方法來(lái)決定對(duì)于是否運(yùn)行。如下列代碼所示組合了和,為運(yùn)行時(shí)異常和斷言錯(cuò)誤屏蔽了不一致的方面,可以向上提供錯(cuò)誤信息和樣例信息。 Junit的工程結(jié)構(gòu) showImg(/img/bVsEeS); 從上圖可以清楚的看出Junit大致分為幾個(gè)版塊,接下來(lái)一一簡(jiǎn)略介紹這些版塊的作用。 runner:定義了Jun...

    Gilbertat 評(píng)論0 收藏0
  • Junit源碼閱讀(五)

    摘要:的作用是包裝從生成的邏輯,提供兩種方案生成和。最后從生成也異常簡(jiǎn)單,也就是實(shí)現(xiàn)其方法返回該。 前言 盡管在第二次博客中我們講述了Runner的運(yùn)行機(jī)制,但是許多其他特性比如Filter是如何與運(yùn)行流程結(jié)合卻并不清楚。這次我們來(lái)回顧整理一下Junit的執(zhí)行流程,給出各種特性生效的機(jī)理,并分析一些代碼中精妙的地方。 Junit的執(zhí)行流程 JUnitCore的RunMain方法,使用jUn...

    vpants 評(píng)論0 收藏0
  • Junit源碼閱讀(六)之Junit設(shè)計(jì)模式

    摘要:前言在這次的博客中我們將著重于的許多集成性功能來(lái)討論中的種種設(shè)計(jì)模式。裝飾器模式裝飾器模式是為了在原有功能上加入新功能,在中絕對(duì)屬于使用最頻繁架構(gòu)中最核心的模式,等都是通過(guò)裝飾器模式來(lái)完成擴(kuò)展的。 前言 在這次的博客中我們將著重于Junit的許多集成性功能來(lái)討論Junit中的種種設(shè)計(jì)模式。可以說(shuō)Junit的實(shí)現(xiàn)本身就是GOF設(shè)計(jì)原則的范例教本,下面就讓我們開(kāi)始吧。 裝飾器模式 裝飾器...

    jlanglang 評(píng)論0 收藏0
  • Junit源碼閱讀(四)之自定義擴(kuò)展

    摘要:前言上次的博客中我們著重介紹了的機(jī)制,這次我們將聚焦到自定義擴(kuò)展上來(lái)。在很多情形下我們需要在測(cè)試過(guò)程中加入一些自定義的動(dòng)作,這些就需要對(duì)進(jìn)行包裝,為此提供了以接口和為基礎(chǔ)的擴(kuò)展機(jī)制。 前言 上次的博客中我們著重介紹了Junit的Validator機(jī)制,這次我們將聚焦到自定義擴(kuò)展Rule上來(lái)。在很多情形下我們需要在測(cè)試過(guò)程中加入一些自定義的動(dòng)作,這些就需要對(duì)statement進(jìn)行包裝,...

    Little_XM 評(píng)論0 收藏0
  • Junit源碼閱讀(三)之精致Validator

    摘要:前言在建立的過(guò)程中,往往需要對(duì)當(dāng)前的測(cè)試樣例和注解進(jìn)行驗(yàn)證,比如檢查測(cè)試類是否含有非靜態(tài)內(nèi)部類,測(cè)試類是否是的。的驗(yàn)證機(jī)制非常精致而優(yōu)美,在本次博客中我們就主要來(lái)談一談機(jī)制的實(shí)現(xiàn)。首先在中定義三個(gè)默認(rèn)的類,如下。 前言 在建立Runner的過(guò)程中,往往需要對(duì)當(dāng)前的測(cè)試樣例和注解進(jìn)行驗(yàn)證,比如檢查測(cè)試類是否含有非靜態(tài)內(nèi)部類,測(cè)試類是否是Public的。Junit的驗(yàn)證機(jī)制非常精致而優(yōu)美...

    李世贊 評(píng)論0 收藏0

發(fā)表評(píng)論

0條評(píng)論

最新活動(dòng)
閱讀需要支付1元查看
<