`

(转)JUnit4学习笔记(四):利用Rule扩展JUnit

 
阅读更多
http://haibin369.iteye.com/blog/2088541

一、Rule简介
Rule是JUnit4中的新特性,它让我们可以扩展JUnit的功能,灵活地改变测试方法的行为。JUnit中用@Rule和@ClassRule两个注解来实现Rule扩展,这两个注解需要放在实现了TestRule借口的成员变量(@Rule)或者静态变量(@ClassRule)上。@Rule和@ClassRule的不同点是,@Rule是方法级别的,每个测试方法执行时都会调用被注解的Rule,而@ClassRule是类级别的,在执行一个测试类的时候只会调用一次被注解的Rule

二、JUnit内置Rule
JUnit4中默认实现了一些常用的Rule:

TemporaryFolder Rule
使用这个Rule可以创建一些临时目录或者文件,在一个测试方法结束之后,系统会自动清空他们。


//创建TemporaryFolder Rule 
//可以在构造方法上加入路径参数来指定临时目录,否则使用系统临时目录 
@Rule 
public TemporaryFolder tempFolder = new TemporaryFolder(); 
 
@Test 
public void testTempFolderRule() throws IOException { 
    //在系统的临时目录下创建文件或者目录,当测试方法执行完毕自动删除 
    tempFolder.newFile("test.txt"); 
    tempFolder.newFolder("test"); 


ExternalResource Rule
ExternalResource 是TemporaryFolder的父类,主要用于在测试之前创建资源,并在测试完成后销毁。

File tempFile; 
 
@Rule 
public ExternalResource extResource = new ExternalResource() { 
    //每个测试执行之前都会调用该方法创建一个临时文件 
    @Override 
    protected void before() throws Throwable { 
        tempFile = File.createTempFile("test", ".txt"); 
    } 
 
    //每个测试执行之后都会调用该方法删除临时文件 
    @Override 
    protected void after() { 
        tempFile.delete(); 
    } 
}; 
 
@Test 
public void testExtResource() throws IOException { 
    System.out.println(tempFile.getCanonicalPath()); 


ErrorCollector Rule
ErrorCollector允许我们收集多个错误,并在测试执行完后一次过显示出来

@Rule 
public ErrorCollector errorCollector = new ErrorCollector(); 
 
@Test 
public void testErrorCollector() { 
    errorCollector.addError(new Exception("Test Fail 1")); 
    errorCollector.addError(new Throwable("fff")); 


Verifier Rule
Verifier是ErrorCollector的父类,可以在测试执行完成之后做一些校验,以验证测试结果是不是正确

String result; 
 
@Rule 
public Verifier verifier = new Verifier() { 
    //当测试执行完之后会调用verify方法验证结果,抛出异常表明测试失败 
    @Override 
    protected void verify() throws Throwable { 
        if (!"Success".equals(result)) { 
            throw new Exception("Test Fail."); 
        } 
    } 
}; 
 
@Test 
public void testVerifier() { 
    result = "Fail"; 


TestWatcher Rule
TestWatcher 定义了五个触发点,分别是测试成功,测试失败,测试开始,测试完成,测试跳过,能让我们在每个触发点执行自定义的逻辑。

@Rule 
public TestWatcher testWatcher = new TestWatcher() { 
    @Override 
    protected void succeeded(Description description) { 
        System.out.println(description.getDisplayName() + " Succeed"); 
    } 
 
    @Override 
    protected void failed(Throwable e, Description description) { 
        System.out.println(description.getDisplayName() + " Fail"); 
    } 
 
    @Override 
    protected void skipped(AssumptionViolatedException e, Description description) { 
        System.out.println(description.getDisplayName() + " Skipped"); 
    } 
 
    @Override 
    protected void starting(Description description) { 
        System.out.println(description.getDisplayName() + " Started"); 
    } 
 
    @Override 
    protected void finished(Description description) { 
        System.out.println(description.getDisplayName() + " finished"); 
    } 
}; 
 
@Test 
public void testTestWatcher() { 
    /*
        测试执行后会有以下输出:
        testTestWatcher(org.haibin369.test.RulesTest) Started
        Test invoked
        testTestWatcher(org.haibin369.test.RulesTest) Succeed
        testTestWatcher(org.haibin369.test.RulesTest) finished
     */ 
    System.out.println("Test invoked"); 


TestName Rule
TestName能让我们在测试中获取目前测试方法的名字。

@Rule 
public TestName testName = new TestName(); 
 
@Test 
public void testTestName() { 
    //打印出测试方法的名字testTestName 
    System.out.println(testName.getMethodName()); 


Timeout与ExpectedException Rule
分别用于超时测试与异常测试,在JUnit4学习笔记(一):基本应用中有提到,这里不再举例。


三、实现原理与部分源码解析
在Junit4的默认Test Runner - org.junit.runners.BlockJUnit4ClassRunner中,有一个methodBlock方法:

protected Statement methodBlock(FrameworkMethod method) { 
    Object test; 
    try { 
        test = new ReflectiveCallable() { 
            @Override 
            protected Object runReflectiveCall() throws Throwable { 
                return createTest(); 
            } 
        }.run(); 
    } catch (Throwable e) { 
        return new Fail(e); 
    } 
 
    Statement statement = methodInvoker(method, test); 
    statement = possiblyExpectingExceptions(method, test, statement); 
    statement = withPotentialTimeout(method, test, statement); 
    statement = withBefores(method, test, statement); 
    statement = withAfters(method, test, statement); 
    statement = withRules(method, test, statement); 
    return statement; 


在JUnit执行每个测试方法之前,methodBlock方法都会被调用,用于把该测试包装成一个Statement。Statement代表一个具体的动作,例如测试方法的执行,Before方法的执行或者Rule的调用,类似于J2EE中的Filter,Statement也使用了责任链模式,将Statement层层包裹,就能形成一个完整的测试,JUnit最后会执行这个Statement。从上面代码可以看到,有以下内容被包装进Statement中:
    1)测试方法的执行;
    2)异常测试,对应于@Test(expected=XXX.class);
    3)超时测试,对应与@Test(timeout=XXX);
    4)Before方法,对应于@Before注解的方法;
    5)After方法,对应于@After注解的方法;
    6)Rule的执行。

在Statement中,可以用evaluate方法控制Statement执行的先后顺序,比如Before方法对应的Statement - RunBefores:

public class RunBefores extends Statement { 
    private final Statement fNext; 
 
    private final Object fTarget; 
 
    private final List<FrameworkMethod> fBefores; 
 
    public RunBefores(Statement next, List<FrameworkMethod> befores, Object target) { 
        fNext = next; 
        fBefores = befores; 
        fTarget = target; 
    } 
 
    @Override 
    public void evaluate() throws Throwable { 
        for (FrameworkMethod before : fBefores) { 
            before.invokeExplosively(fTarget); 
        } 
        fNext.evaluate(); 
    } 

在evaluate中,所有Before方法会先被调用,因为Before方法必须要在测试执行之前调用,然后再执行fNext.evaluate()调用下一个Statement。

理解了Statement,再看回Rule的接口org.junit.rules.TestRule:

public interface TestRule { 
    Statement apply(Statement base, Description description); 

里面只有一个apply方法,用于包裹上级Statement并返回一个新的Statement。因此实现Rule主要是需要实现一个Statement。

四、自定义Rule
通过上面的分析,我们大概知道了如何实现一个Rule,下面是一个例子:

/*
   用于循环执行测试的Rule,在构造函数中给定循环次数。
*/ 
public class LoopRule implements TestRule{ 
    private int loopCount; 
 
    public LoopRule(int loopCount) { 
        this.loopCount = loopCount + 1; 
    } 
 
    @Override 
    public Statement apply(final Statement base, Description description) { 
        return new Statement() { 
            //在测试方法执行的前后分别打印消息 
            @Override 
            public void evaluate() throws Throwable { 
                for (int i = 1; i < loopCount; i++) { 
                    System.out.println("Loop " + i + " started!"); 
                    base.evaluate(); 
                    System.out.println("Loop "+ i + " finished!"); 
                } 
            } 
        }; 
    } 


使用该自定义的Rule:

@Rule 
public LoopRule loopRule = new LoopRule(3); 
 
@Test 
public void testLoopRule() { 
    System.out.println("Test invoked!"); 


执行后打印出以下信息:
Java代码  收藏代码
Loop 1 started! 
Test invoked! 
Loop 1 finished! 
Loop 2 started! 
Test invoked! 
Loop 2 finished! 
Loop 3 started! 
Test invoked! 
Loop 3 finished! 
分享到:
评论

相关推荐

    探索JUnit4扩展:深入Rule

    《探索JUnit4扩展:深入Rule》 JUnit是Java开发者最常用的单元测试框架,它极大地简化了测试代码的编写。在JUnit4中,引入了一个强大的特性——Rule,这使得测试更加灵活且可定制化。本文将深入探讨Rule的概念、...

    探索JUnit4扩展:使用Rule

    标题“探索JUnit4扩展:使用Rule”涉及到的是Java单元测试框架JUnit的一个高级特性,即`@Rule`。在Java开发中,单元测试是确保代码质量、可维护性和可靠性的重要手段,而JUnit作为最流行的Java单元测试框架之一,...

    junit-platform-launcher-1.8.0-M1-API文档-中文版.zip

    赠送jar包:junit-platform-launcher-1.8.0-M1.jar; 赠送原API文档:junit-platform-launcher-1.8.0-M1-javadoc.jar; 赠送源代码:junit-platform-launcher-1.8.0-M1-sources.jar; 赠送Maven依赖信息文件:junit-...

    junit4学习文档

    ### JUnit4 学习知识点详解 #### 一、JUnit4 概述 JUnit4 是 JUnit 测试框架的一个重大更新版本,它充分利用了 Java 5 的注解(Annotation)特性来简化测试用例的编写过程。注解是一种元数据,用于描述程序中的...

    JUnit4学习笔记

    @Test:这是 JUnit4 中的核心元数据,用于标记测试方法。当 JUnit4 运行测试时,会查找所有带有 @Test 注解的方法,并逐一执行。@BeforeClass 和 @AfterClass:这两个元数据分别表示在整个测试类运行前只执行一次的...

    Junit学习笔记~

    Junit学习笔记,希望有用~~~~~~~~~~~~~~~~~~~~~~

    Junit4学习笔记—入门篇.pdf

    ### JUnit4学习笔记——入门篇 #### 一、配置MyEclipse在项目中引入JUnit4.jar包 在使用JUnit4进行单元测试之前,首先需要确保开发环境已正确配置JUnit库。对于使用MyEclipse IDE的开发者来说,可以通过以下步骤...

    JUnit学习笔记

    JUnit4引入了注解驱动的测试,比如`@Before`和`@After`,分别在每个测试方法执行前和执行后运行,通常用于初始化和清理资源。此外,`@BeforeClass`和`@AfterClass`则只在类级别的开始和结束时执行一次。 对于更复杂...

    Junit4完整源码

    JUnit4源码的完整版本包含了整个框架的实现细节,对于理解其工作原理、学习测试驱动开发(TDD)以及进行自定义扩展非常有帮助。 1. **JUnit核心概念**: - **Test Case**:在JUnit4中,测试用例是通过继承`org....

    junit-jupiter-engine-5.8.2-API文档-中文版.zip

    赠送jar包:junit-jupiter-engine-5.8.2.jar; 赠送原API文档:junit-jupiter-engine-5.8.2-javadoc.jar; 赠送源代码:junit-jupiter-engine-5.8.2-sources.jar; 赠送Maven依赖信息文件:junit-jupiter-engine-...

    junit4单元测试报错:method initializationerror not found

    junit4单元测试报错:method initializationerror not found 缺少jar包导致,总共需要三个jar包:junit-4.11.jar,hamcrest-core-1.3.rc2.jar,hamcrest-library-1.3.rc2.jar,都在压缩包里了。

    system-rules-1.18.0.rar所需junit4.jar依赖包

    JUnit 4所需system-rules.jar依赖包,主要包含:system-rules-1.16.1.jar,system-rules-1.17.1.jar,system-rules-1.18.0.jar

    Junit 4.0 学习笔记

    4. 控制器:JUnit提供了一些控制器,如`Assume`,允许我们在运行测试之前进行条件判断,避免无效的测试执行。 5. 运行器(Runner):JUnit 4.0支持自定义运行器,如`BlockJUnit4ClassRunner`是默认的运行器,还有`...

    Junit5.zip

    8. **扩展性**:JUnit5提供了扩展点,如测试监听器、执行器和测试引擎,允许自定义测试行为。 9. **错误消息增强**:JUnit5的错误消息比之前更加详细,有助于更快定位问题。 10. **测试配置**:`@...

    Junit学习.rar

    11. **.junit.extensions**:JUnit 4支持扩展,可以自定义测试行为,例如实现自定义测试监听器。 12. **JUnit与持续集成**:如何将JUnit测试结果集成到持续集成工具如Jenkins、Travis CI等,以实现自动化测试。 13...

    junit-vintage-engine-5.6.2.jar_junit testng

    junit-vintage-engine-5.6.2.jarjunit-vintage-engine-5.6.2.jarjunit-vintage-engine-5.6.2.jar

    单元测试利器JUnit4.docx

    2. JUnit 4的特点:JUnit 4使用Java 5中的注解(annotation)使测试变得更加简单,Martin Fowler评价JUnit:在软件开发领域,从来就没有如此少的代码起到了如此重要的作用。 3. JUnit 4的安装和配置:需要Eclipse、...

    junit-4.13.1.jar

    JUnit 是一个 Java 编程语言的单元测试框架。JUnit 在测试驱动的开发方面有很重要的发展,是起源于 JUnit 的一个统称为 xUnit 的单元测试框架之一。

    junit4教程(《Junit4初探》)

    ## 四、JUnit4的扩展 JUnit4的设计使其易于与其他库和框架集成,例如Mockito用于模拟对象,PowerMock用于测试静态方法和构造函数,或是TestNG提供更高级的测试功能。 ## 五、持续集成与持续测试 在持续集成环境中...

    junit4测试jar包

    10. **扩展性与兼容性**:JUnit4设计得非常开放,可以通过实现`Runner`接口来扩展其功能,例如SpringJUnit4ClassRunner可以结合Spring框架进行测试。同时,JUnit4与大多数IDE和构建工具(如Maven、Gradle)良好集成...

Global site tag (gtag.js) - Google Analytics