tag: junit4.8.2 Source Code
吃柿子专挑软的捏。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