摘要:前言在這次的博客中我們將著重于的許多集成性功能來(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)始吧。
裝飾器模式裝飾器模式是為了在原有功能上加入新功能,在Junit中絕對(duì)屬于使用最頻繁架構(gòu)中最核心的模式,Runner、Filter、Rule等都是通過(guò)裝飾器模式來(lái)完成擴(kuò)展的。下就以Filter的實(shí)現(xiàn)機(jī)制來(lái)說(shuō)明。
首先從命令行中解析出來(lái)的FilterSpec生成各種Filter,如下:
private Request applyFilterSpecs(Request request) { try { for (String filterSpec : filterSpecs) { Filter filter = FilterFactories.createFilterFromFilterSpec( request, filterSpec); request = request.filterWith(filter); } return request; } catch (FilterNotCreatedException e) { return errorReport(e); } }
request的Filter方法將會(huì)返回一個(gè)新的Request,不過(guò)是Request的子類FilterRequest,它依然保留原有的request,只不過(guò)在返回Runner的時(shí)候,再采用Filter過(guò)濾,如下:
public final class FilterRequest extends Request { private final Request request; private final Filter fFilter; 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()))); } } }
Filter的apply方法會(huì)調(diào)用runner自身實(shí)現(xiàn)的filter方法并以自己作為參數(shù),以ParentRunner為例,我們給出filter方法的一般實(shí)現(xiàn)。
public void filter(Filter filter) throws NoTestsRemainException { synchronized (childrenLock) { Listchildren = new ArrayList (getFilteredChildren()); for (Iterator iter = children.iterator(); iter.hasNext(); ) { T each = iter.next(); if (shouldRun(filter, each)) { try { filter.apply(each); } catch (NoTestsRemainException e) { iter.remove(); } } else { iter.remove(); } } filteredChildren = Collections.unmodifiableCollection(children); if (filteredChildren.isEmpty()) { throw new NoTestsRemainException(); } } }
顯然非原子的Runner通過(guò)維護(hù)一個(gè)filteredChildren列表來(lái)提供它所有通過(guò)過(guò)濾的child,每次有新的filter作用到之上,它都需要更新該列表。對(duì)于原子的測(cè)試,它會(huì)提供出它的description,由Filter實(shí)現(xiàn)的shouldRun方法來(lái)判斷是否會(huì)被過(guò)濾,這也是filteredChildren列表更新的原理。
當(dāng)我們先后有多個(gè)Filter時(shí),可以不停地包裝已有的FilterRequest,每個(gè)FilterRequest在getRunnere時(shí)都會(huì)先調(diào)用其內(nèi)部的Request,然后執(zhí)行相同的附加操作,也即更新內(nèi)部Request返回的Runner的filteredChildren列表。使用實(shí)現(xiàn)同一接口繼承同一父類的各個(gè)過(guò)濾器相互嵌套就可以實(shí)現(xiàn)一個(gè)過(guò)濾鏈。
工廠模式工廠模式也在Junit中被大量使用,主要用來(lái)生產(chǎn)各類Rule、Filter等。我們依然以Filter機(jī)制為例來(lái)介紹一次抽象工廠的使用。FilterFactory是一個(gè)工廠接口如下:
public interface FilterFactory { Filter createFilter(FilterFactoryParams params) throws FilterNotCreatedException; @SuppressWarnings("serial") class FilterNotCreatedException extends Exception { public FilterNotCreatedException(Exception exception) { super(exception.getMessage(), exception); } } }
FilterFactories提供了各種由參數(shù)來(lái)生成不同工廠的方法,同時(shí)又使用生成的工廠來(lái)生成Filter,可以說(shuō)在廣義上這就是一個(gè)抽血工廠模式,不過(guò)FilterFactories完成了生產(chǎn)產(chǎn)品的過(guò)程,又集成了提供各類工廠的方法。下面給出代碼大家感受一下:
class FilterFactories { public static Filter createFilterFromFilterSpec(Request request, String filterSpec) throws FilterFactory.FilterNotCreatedException { Description topLevelDescription = request.getRunner().getDescription(); String[] tuple; if (filterSpec.contains("=")) { tuple = filterSpec.split("=", 2); } else { tuple = new String[]{ filterSpec, "" }; } return createFilter(tuple[0], new FilterFactoryParams(topLevelDescription, tuple[1])); } public static Filter createFilter(String filterFactoryFqcn, FilterFactoryParams params) throws FilterFactory.FilterNotCreatedException { FilterFactory filterFactory = createFilterFactory(filterFactoryFqcn); return filterFactory.createFilter(params); } public static Filter createFilter(Class extends FilterFactory> filterFactoryClass, FilterFactoryParams params) throws FilterFactory.FilterNotCreatedException { FilterFactory filterFactory = createFilterFactory(filterFactoryClass); return filterFactory.createFilter(params); } static FilterFactory createFilterFactory(String filterFactoryFqcn) throws FilterNotCreatedException { Class extends FilterFactory> filterFactoryClass; try { filterFactoryClass = Classes.getClass(filterFactoryFqcn).asSubclass(FilterFactory.class); } catch (Exception e) { throw new FilterNotCreatedException(e); } return createFilterFactory(filterFactoryClass); } static FilterFactory createFilterFactory(Class extends FilterFactory> filterFactoryClass) throws FilterNotCreatedException { try { return filterFactoryClass.getConstructor().newInstance(); } catch (Exception e) { throw new FilterNotCreatedException(e); } } }組合模式
在Junit中組合模式主要用于管理Runner和Description的組合。由于測(cè)試的需求十分多樣,有時(shí)需要測(cè)試單個(gè)方法,有時(shí)需要測(cè)試單個(gè)類的所有方法,有時(shí)又需要測(cè)試多個(gè)類的組合方法,所以對(duì)于測(cè)試的表達(dá)必須是強(qiáng)大的。組合模式顯然符合這個(gè)要求,根部的runner會(huì)調(diào)動(dòng)子節(jié)點(diǎn)的run,向上只暴露出根節(jié)點(diǎn)的run方法。由于之前幾篇中對(duì)這一部分論述較多,此處就不再贅述了。
觀察者模式最典型的就是Notifier和Listener,當(dāng)測(cè)試開(kāi)始、結(jié)束、出現(xiàn)錯(cuò)誤時(shí),Notifier將通知它管理的Listener執(zhí)行相應(yīng)的操作,但有趣之處就在于為了能夠處理在通知過(guò)程中出現(xiàn)的異常,Notifer使用了一個(gè)內(nèi)部類SafeNotifier,所有的對(duì)應(yīng)事件(測(cè)試開(kāi)始等)覆寫SafeNotifier里的notifyListener函數(shù),在其中寫調(diào)用Listener的具體哪一個(gè)函數(shù)。相關(guān)的詳細(xì)內(nèi)容請(qǐng)參考上一篇博客,下面給出RunNotifier的源碼(刪去部分內(nèi)容)。
public class RunNotifier { private final List職責(zé)鏈模式listeners = new CopyOnWriteArrayList (); private volatile boolean pleaseStop = false; public void addListener(RunListener listener) { if (listener == null) { throw new NullPointerException("Cannot add a null listener"); } listeners.add(wrapIfNotThreadSafe(listener)); } public void removeListener(RunListener listener) { if (listener == null) { throw new NullPointerException("Cannot remove a null listener"); } listeners.remove(wrapIfNotThreadSafe(listener)); } RunListener wrapIfNotThreadSafe(RunListener listener) { return listener.getClass().isAnnotationPresent(RunListener.ThreadSafe.class) ? listener : new SynchronizedRunListener(listener, this); } private abstract class SafeNotifier { private final List currentListeners; SafeNotifier() { this(listeners); } SafeNotifier(List currentListeners) { this.currentListeners = currentListeners; } void run() { int capacity = currentListeners.size(); List safeListeners = new ArrayList (capacity); List failures = new ArrayList (capacity); for (RunListener listener : currentListeners) { try { notifyListener(listener); safeListeners.add(listener); } catch (Exception e) { failures.add(new Failure(Description.TEST_MECHANISM, e)); } } fireTestFailures(safeListeners, failures); } abstract protected void notifyListener(RunListener each) throws Exception; } public void fireTestRunFinished(final Result result) { new SafeNotifier() { @Override protected void notifyListener(RunListener each) throws Exception { each.testRunFinished(result); } }.run(); } public void fireTestFinished(final Description description) { new SafeNotifier() { @Override protected void notifyListener(RunListener each) throws Exception { each.testFinished(description); } }.run(); } }
可以參考之前的第三篇博客,Junit的Validator機(jī)制需要在三個(gè)層次——類、方法和域上進(jìn)行驗(yàn)證,采用了職責(zé)鏈模式。
文章版權(quán)歸作者所有,未經(jīng)允許請(qǐng)勿轉(zhuǎn)載,若此文章存在違規(guī)行為,您可以聯(lián)系管理員刪除。
轉(zhuǎn)載請(qǐng)注明本文地址:http://systransis.cn/yun/65448.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...
摘要:前言在建立的過(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)美...
摘要:的作用是包裝從生成的邏輯,提供兩種方案生成和。最后從生成也異常簡(jiǎn)單,也就是實(shí)現(xiàn)其方法返回該。 前言 盡管在第二次博客中我們講述了Runner的運(yùn)行機(jī)制,但是許多其他特性比如Filter是如何與運(yùn)行流程結(jié)合卻并不清楚。這次我們來(lái)回顧整理一下Junit的執(zhí)行流程,給出各種特性生效的機(jī)理,并分析一些代碼中精妙的地方。 Junit的執(zhí)行流程 JUnitCore的RunMain方法,使用jUn...
摘要:鑒于它還處在,如果不是著急使用,建議還是使用的,它是遵循規(guī)范的,使用起來(lái)更加方便。貌似要在版本才支持。揭秘讓支持協(xié)議如何啟用命令支持 序 本文主要研究下JEP 110: HTTP/2 Client (Incubator) 基本實(shí)例 sync get /** * --add-modules jdk.incubator.httpclient * @throws ...
摘要:前言上次的博客中我們著重介紹了的機(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)行包裝,...
閱讀 667·2021-09-22 10:02
閱讀 6504·2021-09-03 10:49
閱讀 590·2021-09-02 09:47
閱讀 2178·2019-08-30 15:53
閱讀 2957·2019-08-30 15:44
閱讀 926·2019-08-30 13:20
閱讀 1841·2019-08-29 16:32
閱讀 911·2019-08-29 12:46