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

吃柿子专挑软的捏。JUnit4的核心是org.junit.runner.Runner,但是它涉及的类型太多了,今天看几个简单的类型,清扫它的外围。

JUnit4的输入,是一个或多个(成组测试时)单元测试类的Class对象。为了使用反射机制、为了支持过滤/ filtering和排序/ sorting等,JUnit4进行了一系列预处理。Request、Description和TestClass、FrameworkMethod、FrameworkField等由此产生。

org.junit.runners.model.TestClass

由于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为例(修改了一下原例子——反映参数验证的作用.增加了addx(int x)方法):

package myTest.TestClass;
import static org.junit.Assert.*;
import org.junit.Test;
public class TestUnit{
    public TestUnit(){    }
    @Test
    public void addx(int x){
        assertEquals(5, 1+x);
    }
    @Test
    public void add(){        
        assertEquals(5.0,4.0, 0.1);
    }
    @Test
    public void hello(){        
        assertEquals(5.0,4.0, 0.1);
    }
}
在BlueJ中能够直接运行的只有add()、hello()。

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]

运行结果:

myTest.TestClass.TestUnit

java.lang.AssertionError:expected:<5.0> but was:<4.0>

add invoked!

java.lang.AssertionError:expected:<5.0> but was:<4.0>

hello invoked!

java.lang.IllegalArgumentException:wrong number of arguments

addx invoked!


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)