摘要:前言上次的博客中我們著重介紹了的機(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)行包裝,Junit為此提供了以TestRule接口和RunRules為基礎(chǔ)的Rule擴(kuò)展機(jī)制。
基本模型首選TestRule由注解@ClassRule來(lái)指定,下面我們先給出TestRule的定義。
public interface TestRule { /** * Modifies the method-running {@link Statement} to implement this * test-running rule. * * @param base The {@link Statement} to be modified * @param description A {@link Description} of the test implemented in {@code base} * @return a new statement, which may be the same as {@code base}, * a wrapper around {@code base}, or a completely new Statement. */ Statement apply(Statement base, Description description); }
代碼中的注釋十分清楚,TestRule提供結(jié)合Description為原Statement附加功能轉(zhuǎn)變?yōu)樾碌腟tatement的apply方法。RunRules則是一系列TestRule作用后得到的Statement,如下:
public class RunRules extends Statement { private final Statement statement; public RunRules(Statement base, Iterable原理解釋rules, Description description) { statement = applyAll(base, rules, description); } @Override public void evaluate() throws Throwable { statement.evaluate(); } private static Statement applyAll(Statement result, Iterable rules, Description description) { for (TestRule each : rules) { result = each.apply(result, description); } return result; } }
那么RunRules又是如何在我們的測(cè)試運(yùn)行過(guò)程中被轉(zhuǎn)化的呢?還記得在第二篇博客中我們提到了在classBlock方法中statement會(huì)被withBeforeClasses等裝飾,同樣此處它也被withClassRules裝飾。首先由testClass返回帶@ClassRule注解的對(duì)應(yīng)值,分別由getAnnotatedFieldValues和getAnnotatedMethodValues方法提供。之后我們將這些值轉(zhuǎn)化為T(mén)estRule對(duì)象,然后將這個(gè)TestRule列表和原有的statement結(jié)合返回RunRules。
private Statement withClassRules(Statement statement) { ListTimeOut示例classRules = classRules(); return classRules.isEmpty() ? statement : new RunRules(statement, classRules, getDescription()); }
接下來(lái)我們以超時(shí)擴(kuò)展為示例來(lái)看一看一個(gè)擴(kuò)展是如何起作用的。
public class Timeout implements TestRule { private final long timeout; private final TimeUnit timeUnit; private final boolean lookForStuckThread; public static Builder builder() { return new Builder(); } @Deprecated public Timeout(int millis) { this(millis, TimeUnit.MILLISECONDS); } public Timeout(long timeout, TimeUnit timeUnit) { this.timeout = timeout; this.timeUnit = timeUnit; lookForStuckThread = false; } protected Timeout(Builder builder) { timeout = builder.getTimeout(); timeUnit = builder.getTimeUnit(); lookForStuckThread = builder.getLookingForStuckThread(); } public static Timeout millis(long millis) { return new Timeout(millis, TimeUnit.MILLISECONDS); } public static Timeout seconds(long seconds) { return new Timeout(seconds, TimeUnit.SECONDS); } protected final long getTimeout(TimeUnit unit) { return unit.convert(timeout, timeUnit); } protected final boolean getLookingForStuckThread() { return lookForStuckThread; } protected Statement createFailOnTimeoutStatement( Statement statement) throws Exception { return FailOnTimeout.builder() .withTimeout(timeout, timeUnit) .withLookingForStuckThread(lookForStuckThread) .build(statement); } public Statement apply(Statement base, Description description) { try { return createFailOnTimeoutStatement(base); } catch (final Exception e) { return new Statement() { @Override public void evaluate() throws Throwable { throw new RuntimeException("Invalid parameters for Timeout", e); } }; } } public static class Builder { private boolean lookForStuckThread = false; private long timeout = 0; private TimeUnit timeUnit = TimeUnit.SECONDS; protected Builder() { } public Builder withTimeout(long timeout, TimeUnit unit) { this.timeout = timeout; this.timeUnit = unit; return this; } protected long getTimeout() { return timeout; } protected TimeUnit getTimeUnit() { return timeUnit; } public Builder withLookingForStuckThread(boolean enable) { this.lookForStuckThread = enable; return this; } protected boolean getLookingForStuckThread() { return lookForStuckThread; } /** * Builds a {@link Timeout} instance using the values in this builder., */ public Timeout build() { return new Timeout(this); } } }
我們可以看到上述最核心的就是createFailOnTimeoutStatement方法,它直接返回了一個(gè)FailOnTimeout,并且用它內(nèi)建的Builder初始化。下面我們僅僅給出FailOnTimeout內(nèi)部的域以及一些核心方法。
public class FailOnTimeout extends Statement { private final Statement originalStatement; private final TimeUnit timeUnit; private final long timeout; private final boolean lookForStuckThread; private FailOnTimeout(Builder builder, Statement statement) { originalStatement = statement; timeout = builder.timeout; timeUnit = builder.unit; lookForStuckThread = builder.lookForStuckThread; } public static class Builder { private boolean lookForStuckThread = false; private long timeout = 0; private TimeUnit unit = TimeUnit.SECONDS; private Builder() { } public FailOnTimeout build(Statement statement) { if (statement == null) { throw new NullPointerException("statement cannot be null"); } return new FailOnTimeout(this, statement); } } @Override public void evaluate() throws Throwable { CallableStatement callable = new CallableStatement(); FutureTasktask = new FutureTask (callable); ThreadGroup threadGroup = new ThreadGroup("FailOnTimeoutGroup"); Thread thread = new Thread(threadGroup, task, "Time-limited test"); thread.setDaemon(true); thread.start(); callable.awaitStarted(); Throwable throwable = getResult(task, thread); if (throwable != null) { throw throwable; } } private Throwable getResult(FutureTask task, Thread thread) { try { if (timeout > 0) { return task.get(timeout, timeUnit); } else { return task.get(); } } catch (InterruptedException e) { return e; // caller will re-throw; no need to call Thread.interrupt() } catch (ExecutionException e) { // test failed; have caller re-throw the exception thrown by the test return e.getCause(); } catch (TimeoutException e) { return createTimeoutException(thread); } } private class CallableStatement implements Callable { private final CountDownLatch startLatch = new CountDownLatch(1); public Throwable call() throws Exception { try { startLatch.countDown(); originalStatement.evaluate(); } catch (Exception e) { throw e; } catch (Throwable e) { return e; } return null; } public void awaitStarted() throws InterruptedException { startLatch.await(); } } }
可以看出它通過(guò)內(nèi)置的Builder類來(lái)配置參數(shù),通過(guò)CallableStatement和FutureTask啟動(dòng)新線程來(lái)運(yùn)行真實(shí)的測(cè)試樣例,并使用CountDownLatch來(lái)讓父進(jìn)程等待。實(shí)際的超時(shí)判斷則借助了FutureTask的getResult,如果規(guī)定時(shí)間未返回結(jié)果就拋出超時(shí)異常。
文章版權(quán)歸作者所有,未經(jīng)允許請(qǐng)勿轉(zhuǎn)載,若此文章存在違規(guī)行為,您可以聯(lián)系管理員刪除。
轉(zhuǎn)載請(qǐng)注明本文地址:http://systransis.cn/yun/65450.html
摘要:是對(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...
摘要:前言在這次的博客中我們將著重于的許多集成性功能來(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)始吧。 裝飾器模式 裝飾器...
摘要:自調(diào)用匿名函數(shù)打開(kāi)源碼,首先你會(huì)看到這樣的代碼結(jié)構(gòu)這是一個(gè)自調(diào)用匿名函數(shù)。這樣子最大程度防止外界的變量定義對(duì)內(nèi)部造成影響 自調(diào)用匿名函數(shù) 打開(kāi)jQuery源碼,首先你會(huì)看到這樣的代碼結(jié)構(gòu): (function(window,undefined){ //jquery code })(window); 這是一個(gè)自調(diào)用匿名函數(shù)。在第一個(gè)括號(hào)內(nèi),創(chuàng)建一個(gè)匿名函數(shù);第二個(gè)括號(hào)內(nèi),立...
摘要:前言在建立的過(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)美...
閱讀 1648·2021-10-12 10:11
閱讀 3764·2021-09-03 10:35
閱讀 1446·2019-08-30 15:55
閱讀 2137·2019-08-30 15:54
閱讀 1004·2019-08-30 13:07
閱讀 1018·2019-08-30 11:09
閱讀 584·2019-08-29 13:21
閱讀 2655·2019-08-29 11:32