首页 > 代码库 > JUnit4.8.2源代码分析-3 TestClass 和RunnerBuilder

JUnit4.8.2源代码分析-3 TestClass 和RunnerBuilder

吃柿子专挑软的捏。JUnit4的核心是org.junit.runner.Runner,它涉及的类型太多,今天看几个简单的类型。看完了而且不准备回头再看的类型,yqj2065会在BlueJ中将删除。删除如NullBuilder时,将import org.junit.internal.builders.NullBuilder加到本包的它的客户类中(其他包使用的,是BlueJ库中引入的包文件中的类),以保证整个项目可以编译和生成JavaDoc。

org.junit.runners.model.TestClass

JUnit4的输入,严格地说是一个或多个(组)单元测试类的Class对象。由于JDK中对标注的处理代价高昂,TestClass事先对单元测试类的标注相关的内容提取出来,1便于处理,2尽量共享。TestClass的3个成员变量

private final Class<?> fClass;
private Map<Class<?>, List<FrameworkMethod>> fMethodsForAnnotations= new HashMap<>();
private Map<Class<?>, List<FrameworkField>> fFieldsForAnnotations= new HashMap<>();

fClass是TestClass封装的单元测试类的Class对象。public Class<?> getJavaClass() 返回的就是它,而String getName()是它映射的单元测试类的类全名(或"null")。

fMethodsForAnnotations  每一种标识与被该标注修饰的方法的封装类的List即List<FrameworkMethod>的键值对。如果JUnit4设计一个标记接口JUnit4Annotation,其类型定义可以更小一些 如 Map<Class<? extends JUnit4Annotation>, List<FrameworkMethod> >。要是我,会这么做。

fFieldsForAnnotations 暂时不想看它,for Rules。

TestClass的构造器初始化3个成员变量。它要求单元测试类只能够有一个构造器。通过反射,构造器将单元测试类的所有祖先类放在一个List<Class<?>>中,找到它们每一个声明的方法,并按照方法的标注填入fMethodsForAnnotations和fFieldsForAnnotations。

而曝露出来的接口则是按照标注(key)获得Map中映射的List。

public List<FrameworkMethod> getAnnotatedMethods(Class<? extends Annotation> annotationClass) 

public List<FrameworkField> getAnnotatedFields(  Class<? extends Annotation> annotationClass) 

以及

public <T> List<T> getAnnotatedFieldValues(Object test,Class<? extends Annotation> annotationClass, Class<T> valueClass) 暂时忽略

public Annotation[] getAnnotations()

public Constructor<?> getOnlyConstructor()

TestClass的源代码比较容易看懂,目标清晰的代码都容易懂。

相关的类:FrameworkMethod、FrameworkField和它们的父类FrameworkMember<T>

FrameworkMethod

封装一个被标注的方法,标注为@Test 、@Before、@After、@BeforeClass、@AfterClass、@Ignore等。

以单元测试类TestUnit为例:

package myTest.TestClass;
import myTest.HelloWorld;
import static org.junit.Assert.*;
import org.junit.Test;
public class TestUnit{
    public TestUnit(){    }
    @Test
    public void add(){
        HelloWorld h = new HelloWorld();
        assertEquals(5.0, h.add(1, 2), 0.1);
    }
    @Test
    public void hello(){        
        assertEquals(5.0,4.0, 0.1);
    }
}
虽然可以直接运行测试各个@Test,我们还是编写demo程序,

package myTest.TestClass;
import static tool.Print.*;
import java.util.List;
import java.lang.reflect.*;
import org.junit.runners.model.*;
import org.junit.Test;
/**
 * org.junit.runners.model.FrameworkMethod
 * 封装一个被测试的方法
 * @Test 、@Before、@After、@BeforeClass、@AfterClass、@Ignore
 */
public class TestClassDemo{
    public static void test()throws Throwable{
        TestClass klass = new TestClass(TestUnit.class);
        pln(klass.getName() );
        List<FrameworkMethod> list = klass.getAnnotatedMethods(Test.class);
        for(FrameworkMethod fm :list){
            try {
                fm.invokeExplosively((TestUnit)klass.getJavaClass().newInstance(), new Object[0]) ;
            }catch (Throwable e) {
                pln(e);
            }finally{
                pln(fm.getName()+" invoked!" );
            }
        }
    }
}
注意:invokeExplosively(final Object target, final Object... params)用于调用单元测试类的@Test方法,参数target为(TestUnit)klass.getJavaClass().newInstance(),通常的测试方法的没有参数,取new Object[0]

org.junit.runners.model.RunnerBuilder

单元测试类可能使用各种@Target(ElementType.TYPE)的标注如@Ignore、@RunWith、@SuiteClasse。org.junit.runners.model.RunnerBuilder针对这些标注产生不同的Runner。RunnerBuilder虽然取名builder,其实是工厂方法

public abstract Runner runnerForClass(Class<?>testClass) throws Throwable; //工厂方法

1. NullBuilder  如同定义数学的0一样。@Override Runner runnerForClass(Class<?>)返回null。删除

2. IgnoredBuilder  如果测试类由@Ignore标注,生成一个Runner子类IgnoredClassRunner对象。删除

3. AnnotatedBuilder  如果测试类由@RunWith标注,生成一个Runner对象。

4.兼容用JUnit3Builder   如果测试类使用JUnit3风格,生成一个Unit38ClassRunner对象。删除

5. JUnit4Builder  如果测试类使用JUnit4风格,生成一个BlockJUnit4ClassRunner对象。删除

6. SuiteMethodBuilder  组的问题保留

7. AllDefaultPossibilitiesBuilder 合集。按照IgnoredBuilder、AnnotatedBuilder、SuiteMethodBuilder(如果不使用组,则返回NullBuilder)、JUnit3Builder和JUnit4Builder的顺序创建各种RunnerBuilder,先调用RunnerBuilder.safeRunnerForClass方法再判断一个RunnerBuilder是否为null,非空则是AllDefaultPossibilitiesBuilder将使用的RunnerBuilder。



JUnit4.8.2源代码分析-3 TestClass 和RunnerBuilder