【JUnit4.10源代码分析】6.1 排序和过滤

 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));
    }

排序的很简单,略。


你可能感兴趣的:(框架,源代码,JUnit4.10)