首页 > 代码库 > 【JUnit4.10源代码分析】3.4 Description与测试树

【JUnit4.10源代码分析】3.4 Description与测试树

Description使用组合模式描述一个测试树。组合模式中所有元素都是Composite对象。

Description有成员变量private final ArrayList<Description>fChildren= newArrayList<Description>(); //无元素

保存其子结点。fChildren非空,所以任何子结点都是一个Composite,但是this. getChildren().size()为0的结点,事实上就是叶子。

测试树

一颗测试树Description,有诸多Description构成。每一个Description包含的数据:

       privatefinal ArrayList<Description> fChildren= newArrayList<Description>(); //无元素

       privatefinal String fDisplayName;      

       privatefinal Annotation[] fAnnotations;

叶子结点有:一个被测试的方法(atomic/ a single test),Description类中定义的的两个命名常量EMPTY(名字为"No Tests")和TEST_MECHANISM(名字为"Test mechanism")

一般元素/Composite,重点是fChildren的构造。一个单元测试类,其Description的子结点包括所有@test修饰的方法(不包括@Before等修饰的方法);一个成组测试类的子结点包括几个单元测试类。例如有Unit1、Unit2、Unit3,而SuiteUnit将Unit2、Unit3组成一组。

package units;
import static tool.Print.*;
import org.junit.*;//各种标注

public class Unit1{
    public Unit1() { }
    @Before public void setUp(){    }
    @After  public void tearDown(){  }
    @Test   public void m1(){
        pln("Unit1.m1()");
    }
    @Test @Ignore  public void m2(){
        pln("Unit1.m2()");
    }
    @Test public void m3(){
        pln("Unit1.m3()");
    }
}
package units;
public class Unit2 {
    @org.junit.Test
    public void test2() {	
        System.out.println("Unit2.test2()");
    }
}
package units;
public class Unit3 {
    @org.junit.Test
    public void testSth() {<span style="white-space:pre">	</span>
        System.out.println("Unit3.testSth()"); 
    }
}
SuiteUnit 的代码:

package units;
import org.junit.runner.RunWith;
import org.junit.runners.Suite;
@RunWith(Suite.class)
@Suite.SuiteClasses({
   Unit2.class,
   Unit3.class,
})
public class SuiteUnit {}  
先看一个例子,打印测试 Request.classes(Unit1.class,SuiteUnit.class)时的测试树。

package demo;
import static tool.Print.*;
import units.Unit1;
import units.SuiteUnit;
import org.junit.runner.Description;
import org.junit.runner.Request;
import org.junit.runner.Runner;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
/**
 *测试Description的各种用法
 * @author yqj2065
 */
public class DescriptionDemo {
    public static void tree(){
        Request rqst = Request.classes(Unit1.class,SuiteUnit.class);
        Runner r=rqst.getRunner();
        Description descr = r.getDescription();
        String prefix = "";
        print(descr,prefix);        
        pln( "the total number of atomic tests = "+descr.testCount() );//the total number of atomic tests.
    }
    public static void print(Description now,String prefix){ 
        pln(prefix+ now.getDisplayName() );        
        if(now.isSuite()) {
            prefix+="  ";
            for (Description x : now.getChildren()){                
                print(x,prefix);
            }
        }
    }
    public static void main(String... args) {
        tree();
     }
}

输出:

null
  units.Unit1
    m1(units.Unit1)
    m2(units.Unit1)
    m3(units.Unit1)
  units.SuiteUnit
    units.Unit2
      test2(units.Unit2)
    units.Unit3
      testSth(units.Unit3)
the total number of atomic tests = 5

此时的测试树有两个子结点:单元测试类units.Unit1(的Description)和成组测试类units.SuiteUnit。单元测试类的子结点都是叶子;而units.SuiteUnit的子结点为包含的单元测试类。

相关方法

String getDisplayName():返回fDisplayName。本描述的用于打印的名字,一般情况下都采用类全名或JUnit的方法字符串如method(所属类的全名)
String getClassName()、String getMethodName():解析方法字符串,获得@Test修饰的方法相关的类全名和方法名;Class<?> getTestClass(),由getClassName()的返回值为参数name调用Class.forName(name);
 
ArrayList<Description> getChildren():返回fChildren。
void addChild(Description description)
isTest()(是否叶子)、isSuite()(是否组合):互斥的一对判断
isEmpty()
 
Collection<Annotation> getAnnotations():将fAnnotations数组形式的转换为Collection;本Description所对应元素前使用的标注
<T extends Annotation> T getAnnotation(Class<T> annotationType)。fAnnotations中是否含有annotationType。
 
@Override hashCode()、equals(Object obj)、toString();
 
组合模式中的Operation()的对应物为int testCount(),包含的叶子测试的总数。

构造器

Description有一个私有构造器,禁止客户类直接创建Description。

private Description(final String displayName, Annotation... annotations) {
fDisplayName= displayName;
fAnnotations= annotations;
}

然而,Description的构造,它提供了静态方法获得Description对象。这些静态方法构造本Description的基本信息,不添加子结点。因而4个静态方法都是调用私有构造器,

public static Description createSuiteDescription(String name, Annotation... annotations)
public static Description createSuiteDescription(Class<?> testClass) 
public static Description createTestDescription(Class<?> clazz, String name, Annotation... annotations) {
return new Description(String.format("%s(%s)", name, clazz.getName()), annotations);
}
public static Description createTestDescription(Class<?> clazz, String name) {
return createTestDescription(clazz, name, new Annotation[0]);
}

其中String str = String.format("%s(%s)", "m1", "Class1");

str为m1(Class1),JUnit的方法字符串如method(所属类的全名)

DescriptionDemo中测试树是如何构建的呢?ParentRunner<T>的代码

	@Override
	public Description getDescription() {
		Description description= Description.createSuiteDescription(getName(),
				getRunnerAnnotations());
		for (T child : getFilteredChildren())
			description.addChild(describeChild(child));
		return description;
	}
可以通过“调试文件”,跟踪查看。




【JUnit4.10源代码分析】3.4 Description与测试树