摘要:前言在這次的博客中我們將著重于的許多集成性功能來討論中的種種設計模式。裝飾器模式裝飾器模式是為了在原有功能上加入新功能,在中絕對屬于使用最頻繁架構中最核心的模式,等都是通過裝飾器模式來完成擴展的。
前言
在這次的博客中我們將著重于Junit的許多集成性功能來討論Junit中的種種設計模式。可以說Junit的實現(xiàn)本身就是GOF設計原則的范例教本,下面就讓我們開始吧。
裝飾器模式裝飾器模式是為了在原有功能上加入新功能,在Junit中絕對屬于使用最頻繁架構中最核心的模式,Runner、Filter、Rule等都是通過裝飾器模式來完成擴展的。下就以Filter的實現(xiàn)機制來說明。
首先從命令行中解析出來的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方法將會返回一個新的Request,不過是Request的子類FilterRequest,它依然保留原有的request,只不過在返回Runner的時候,再采用Filter過濾,如下:
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方法會調用runner自身實現(xiàn)的filter方法并以自己作為參數,以ParentRunner為例,我們給出filter方法的一般實現(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通過維護一個filteredChildren列表來提供它所有通過過濾的child,每次有新的filter作用到之上,它都需要更新該列表。對于原子的測試,它會提供出它的description,由Filter實現(xiàn)的shouldRun方法來判斷是否會被過濾,這也是filteredChildren列表更新的原理。
當我們先后有多個Filter時,可以不停地包裝已有的FilterRequest,每個FilterRequest在getRunnere時都會先調用其內部的Request,然后執(zhí)行相同的附加操作,也即更新內部Request返回的Runner的filteredChildren列表。使用實現(xiàn)同一接口繼承同一父類的各個過濾器相互嵌套就可以實現(xiàn)一個過濾鏈。
工廠模式工廠模式也在Junit中被大量使用,主要用來生產各類Rule、Filter等。我們依然以Filter機制為例來介紹一次抽象工廠的使用。FilterFactory是一個工廠接口如下:
public interface FilterFactory { Filter createFilter(FilterFactoryParams params) throws FilterNotCreatedException; @SuppressWarnings("serial") class FilterNotCreatedException extends Exception { public FilterNotCreatedException(Exception exception) { super(exception.getMessage(), exception); } } }
FilterFactories提供了各種由參數來生成不同工廠的方法,同時又使用生成的工廠來生成Filter,可以說在廣義上這就是一個抽血工廠模式,不過FilterFactories完成了生產產品的過程,又集成了提供各類工廠的方法。下面給出代碼大家感受一下:
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的組合。由于測試的需求十分多樣,有時需要測試單個方法,有時需要測試單個類的所有方法,有時又需要測試多個類的組合方法,所以對于測試的表達必須是強大的。組合模式顯然符合這個要求,根部的runner會調動子節(jié)點的run,向上只暴露出根節(jié)點的run方法。由于之前幾篇中對這一部分論述較多,此處就不再贅述了。
觀察者模式最典型的就是Notifier和Listener,當測試開始、結束、出現(xiàn)錯誤時,Notifier將通知它管理的Listener執(zhí)行相應的操作,但有趣之處就在于為了能夠處理在通知過程中出現(xiàn)的異常,Notifer使用了一個內部類SafeNotifier,所有的對應事件(測試開始等)覆寫SafeNotifier里的notifyListener函數,在其中寫調用Listener的具體哪一個函數。相關的詳細內容請參考上一篇博客,下面給出RunNotifier的源碼(刪去部分內容)。
public class RunNotifier { private final List職責鏈模式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機制需要在三個層次——類、方法和域上進行驗證,采用了職責鏈模式。
文章版權歸作者所有,未經允許請勿轉載,若此文章存在違規(guī)行為,您可以聯(lián)系管理員刪除。
轉載請注明本文地址:http://specialneedsforspecialkids.com/yun/65448.html
摘要:是對測試樣例的建模,用來組合多個測試樣例,是中的核心內容。也是一個虛類,子類應該實現(xiàn)方法來決定對于是否運行。如下列代碼所示組合了和,為運行時異常和斷言錯誤屏蔽了不一致的方面,可以向上提供錯誤信息和樣例信息。 Junit的工程結構 showImg(/img/bVsEeS); 從上圖可以清楚的看出Junit大致分為幾個版塊,接下來一一簡略介紹這些版塊的作用。 runner:定義了Jun...
摘要:前言在建立的過程中,往往需要對當前的測試樣例和注解進行驗證,比如檢查測試類是否含有非靜態(tài)內部類,測試類是否是的。的驗證機制非常精致而優(yōu)美,在本次博客中我們就主要來談一談機制的實現(xiàn)。首先在中定義三個默認的類,如下。 前言 在建立Runner的過程中,往往需要對當前的測試樣例和注解進行驗證,比如檢查測試類是否含有非靜態(tài)內部類,測試類是否是Public的。Junit的驗證機制非常精致而優(yōu)美...
摘要:的作用是包裝從生成的邏輯,提供兩種方案生成和。最后從生成也異常簡單,也就是實現(xiàn)其方法返回該。 前言 盡管在第二次博客中我們講述了Runner的運行機制,但是許多其他特性比如Filter是如何與運行流程結合卻并不清楚。這次我們來回顧整理一下Junit的執(zhí)行流程,給出各種特性生效的機理,并分析一些代碼中精妙的地方。 Junit的執(zhí)行流程 JUnitCore的RunMain方法,使用jUn...
摘要:鑒于它還處在,如果不是著急使用,建議還是使用的,它是遵循規(guī)范的,使用起來更加方便。貌似要在版本才支持。揭秘讓支持協(xié)議如何啟用命令支持 序 本文主要研究下JEP 110: HTTP/2 Client (Incubator) 基本實例 sync get /** * --add-modules jdk.incubator.httpclient * @throws ...
摘要:前言上次的博客中我們著重介紹了的機制,這次我們將聚焦到自定義擴展上來。在很多情形下我們需要在測試過程中加入一些自定義的動作,這些就需要對進行包裝,為此提供了以接口和為基礎的擴展機制。 前言 上次的博客中我們著重介紹了Junit的Validator機制,這次我們將聚焦到自定義擴展Rule上來。在很多情形下我們需要在測試過程中加入一些自定義的動作,這些就需要對statement進行包裝,...
閱讀 634·2021-09-22 10:02
閱讀 6326·2021-09-03 10:49
閱讀 565·2021-09-02 09:47
閱讀 2151·2019-08-30 15:53
閱讀 2929·2019-08-30 15:44
閱讀 900·2019-08-30 13:20
閱讀 1812·2019-08-29 16:32
閱讀 889·2019-08-29 12:46