abstract class ParentRunner<T> extends Runner implements Filterable,Sortable
本节介绍排序和过滤。
(尽管JUnit4.8.2源码分析-6.1 排序和过滤中演示了客户使用排序和过滤的方式,也有些不明确其设计意图。可是。先读懂源码为妙。说不定看着看着就明确了。
)
org.junit.runner.manipulation包
排序和过滤的相关类型。在org.junit.runner.manipulation包中。Sortable和Filterable是两个依赖注入接口。而Sorter和Filter的apply是注入方法。
1.比如Sortable是一个函数接口。yqj2065认为Sortable.sort(Sorter)不如叫Sortable.setSorter(Sorter)。
package org.junit.runner.manipulation; public interface Sortable { public void sort(Sorter sorter); }2.Filterable类似,定义了void filter(Filter filter) throws NoTestsRemainException;
3.NoTestsRemainException抱怨filter 将全部的測试都过滤掉了。
4.Sorter implements Comparator<Description>。我们知道。java.util.Comparator接口是一个策略类,定义了int compare(T o1, T o2)方法。而Sorter是一个简单的代理模式的Proxy角色。
通过构造器注入的方式Sorter(Comparator<Description>comparator)初始化realSubject角色。測试程序猿能够定义自己的排序器。并且。Sorter应用Null
Object模式,定义了NULL静态内部子类。
package org.junit.runner.manipulation; import java.util.Comparator; import org.junit.runner.Description; public class Sorter implements Comparator<Description> { /** * NULL is a <code>Sorter</code> that leaves elements in an undefined order */ public static Sorter NULL= new Sorter(new Comparator<Description>() { public int compare(Description o1, Description o2) { return 0; }});
public void apply(Object object) { if (object instanceof Sortable) { Sortable sortable = (Sortable) object; sortable.sort(this); } } }Sorter的方法apply(Object object),将this应用于Object 身上。
5.Filter是一个抽象类。当须要过滤某些測试时。測试程序猿要定义自己的过滤器。
Filter应用Null Object模式,定义了ALL静态内部子类。静态工厂生成public static Filter matchMethodDescription(final Description desiredDescription)。而public Filter intersect(final Filter second)同意多个Filter串接。
依照上述几个样例,我们能够写出自己的过滤器。
package sortFilter; import java.util.Arrays; import java.util.HashSet; import java.util.Set; import org.junit.runner.Description; import org.junit.runner.manipulation.Filter; /** *排除指定的方法。 * @author yqj2065 */ public class MethodNameFilter extends Filter { private final Set<String> excludedMethods = new HashSet<>(); public MethodNameFilter(String... excludedMethods) { this.excludedMethods.addAll(Arrays.asList(excludedMethods)); } @Override public boolean shouldRun(Description description) { String methodName = description.getMethodName(); return !excludedMethods.contains(methodName); } @Override public String describe() { return this.getClass().getSimpleName() + "-excluded methods: " + excludedMethods; } }Unit4有測试方法a() 、@Ignore b() 、c() 和d()等,
package demo; import static tool.Print.*; import org.junit.runner.manipulation.NoTestsRemainException; import org.junit.runner.notification.RunNotifier; import org.junit.runners.BlockJUnit4ClassRunner; import org.junit.runners.model.InitializationError; import sortFilter.MethodNameFilter; import units.Unit4; /** * * @author yqj2065 */ public class FilterDemo { public static void main(String... args) { try { BlockJUnit4ClassRunner runner = null; try { runner = new BlockJUnit4ClassRunner(Unit4.class); } catch (InitializationError e) { } runner.filter(new MethodNameFilter("a","b","c", "d"));//排除全部 runner.run(new RunNotifier()); } catch (NoTestsRemainException ex) { pln(ex); } } }输出:org.junit.runner.manipulation.NoTestsRemainException
保留@Ignore b(),尽管没有运行測试。可是不抛出异常。
ParentRunner<T>与排序和过滤
ParentRunner implements Filterable,Sortable
注意到:JUnit4.8.2中ParentRunner 有域Filter fFilter= null;而JUnit4.10不再有该域。而是以过滤后的private List<T> fFilteredChildren= null代替。
因为ParentRunner<T>的类型參数T,代表其某种孩子的类型。BlockJUnit4ClassRunner的T为FrameworkMethod。Suite的T为Runner。
在ParentRunner中统一地处理T显得比較复杂。并且JUnit4.10代码变化较大。
过滤相关的代码
getFilteredChildren()惰性初始化域fFilteredChildren,当中调用getChildren()由子类给出实现,返回List<T>。@Override filter(Filter filter),就须要差别对待FrameworkMethod和Runner。
1.JUnit没有使用instanceof。而是利用abstract Description describeChild(T child),无论T是FrameworkMethod还是Runner。总能够构造(FrameworkMethod)或获得(Runner)一个Description返回值;
2.而Filter定义的public abstract boolean shouldRun(Description description),能够统一处理两者。
3.因而。ParentRunner定义了boolean shouldRun(Filter filter, T each) ,调用前者。
可是,从实际參数看,filter.shouldRun(FrameworkMethod的Description )才进行比較和过滤;而filter.shouldRun(Runner.getDescription ),总是返回true、
于是,第5行代码if (shouldRun(filter, each))出现有意思的情况。
对于FrameworkMethod each。假设shouldRun,其代码应该为
try {
//
} catch (NoTestsRemainException e) {
iter.remove();
}
由于filter.apply(FrameworkMethod )肯定直接return;而shouldRun推断不通过,就意味着iter.remove();在fFilteredChildren中删除该方法;
对于Runner each。总是shouldRun,并且运行filter.apply(each);从而调用到each.filter(filter);能够看出。假设没有FrameworkMethod ,能够直接运行each.filter(filter);可是如今不得不打乒乓球。利用filter.apply(each)排除掉FrameworkMethod
。
private List<T> fFilteredChildren= null; public void filter(Filter filter) throws NoTestsRemainException { for (Iterator<T> iter = getFilteredChildren().iterator(); iter.hasNext(); ) { T each = iter.next(); if (shouldRun(filter, each)) try { filter.apply(each); } catch (NoTestsRemainException e) { iter.remove(); } else iter.remove(); } if (getFilteredChildren().isEmpty()) { throw new NoTestsRemainException(); } } private List<T> getFilteredChildren() { if (fFilteredChildren == null) fFilteredChildren = new ArrayList<T>(getChildren()); return fFilteredChildren; } private boolean shouldRun(Filter filter, T each) { return filter.shouldRun(describeChild(each)); }
作为比較,JUnit4.8.2的相关代码列在以下,有时间再看。
private Filter fFilter= null; public void filter(Filter filter) throws NoTestsRemainException { fFilter= filter; for (T each : getChildren()) if (shouldRun(each)) return; throw new NoTestsRemainException(); } private List<T> getFilteredChildren() { ArrayList<T> filtered= new ArrayList<T>(); for (T each : getChildren()) if (shouldRun(each)) try { filterChild(each); sortChild(each); filtered.add(each); } catch (NoTestsRemainException e) { // don't add it } Collections.sort(filtered, comparator()); return filtered; } private void filterChild(T child) throws NoTestsRemainException { if (fFilter != null) fFilter.apply(child); } private boolean shouldRun(T each) { return fFilter == null || fFilter.shouldRun(describeChild(each)); }
排序的非常easy。略。