`
javasogo
  • 浏览: 1821456 次
  • 性别: Icon_minigender_1
  • 来自: 北京
文章分类
社区版块
存档分类
最新评论

了解 JUnit 核心类、接口及生命周期

阅读更多

Junit 从问世至今已有 12 年的历史,期间功能不断完善,用户逐渐扩大,已经成为 Java 软件开发中应用最为广泛的测试框架。本文着重介绍 JUnit 的核心接口、核心类以及 TestCase 的生命周期,以便读者从架构层面掌握这个工具。

1997 年,Erich Gamma 和 Kent Beck 为 Java 语言创建了一个简单但有效的单元测试框架,称作 JUnit。JUnit 很快成为 Java 中开发单元测试的框架标准。世界上无数软件项目使用它。本文将介绍 JUnit 的核心接口,核心类以及 JUnit 的生命周期。

JUnit 核心接口及核心类

了解 JUnit 的生命周期之前,先了解 JUnit 的核心接口和类是有必要的,这对于了解 TestCase 的生命周期有很大的帮助。

  • Test:是 TestCase、TestSuite 的共同接口。run(TestResult result) 用来运行 Test,并且将结果保存到 TestResult。

  • TestCase:Test 的接口的抽象实现,是 Abstract 类,所以不能实例化,能被继承。其中一个构造函数 TestCase(String name),根据输入的参数,创建一个测试实例。参数为该类的以 test 开头的方法名,把它添加到 TestSuite 中,指定仅仅运行 TestCase 中的一个方法。

  • TestSuite:实现 Test 接口。可以组装一个或者多个 TestCase。待测试类中可能包括了对被测类的多个 TestCase,而 TestSuit 可以保存多个 TestCase,负责收集这些测试,这样就可以一个 Suite 就能运行对被测类的多个测试。

  • TestResult:保存 TestCase 运行中的事件。TestResult 有 List<TestFailure> fFailuresList<TestFailure> fErrors。fFailures 记录 Test 运行中的 AssertionFailedError,而 fErrors 则记录 Exception。Failure 是当期望值和断言不匹配的时候抛出的异常,而 Error 则是不曾预料到的异常,如:ArrayIndexOutOfBoundsException。

  • TestListener:是个接口,对事件监听,可供 TestRunner 类使用。

  • ResultPrinter:实现 TestListener 接口。在 TestCase 运行过程中,对所监听的对象的事件以一定格式及时输出,运行完后,对 TestResult 对象进行分析,输出的统计结果。

  • BaseTestRunner:所有 TestRunner 的超类。

  • java Junit.swingui.TestRunner:实现 BaseTestRunner,提供图形界面。从 4.0 版本起,就没有再提供这个类。这是 4.0 版本和之前版本的显著变化之一。

  • java Junit.textui.TestRunner:实现 BaseTestRunner,提供文本界面。下面将以它做为例子讲解 JUnit 生命周期。

TestCase 实例

了解了前面的几个类,下面将看一个例子:

两种不同参数运行 TestCase

参数 1:

输入:

输出:

Up 
testPay! 
Down 
Up 
testPayWithDiscount! 
Down 

参数 2:

输入:

输出:

Up 
testPayWithDiscount! 
Down

参数 1:TestCase 名字,该类的所有的以 test 开头的 public 方法都会执行。

参数 2:参数 -m,仅仅运行该类的该方法。

TestRunner 还提供了其他的参数 -wait:(最大响应时间),-v:查看 JUnit 版本号。从输出可以看出,参数一: testPay()testPayWithDiscount() 都运行;参数二:仅仅运行参数中的 testPayWithDiscount()。对比两个输出结果,setUp() 在每个方法运行前运行一次,teardown() 在每个方法运行后执行一次。后面将会详细介绍。

TestRunner 处理两种不同的参数

TestRunner main() 方法中,生成一个 TestRunner 实例,调用 start(args) 方法。在 start 方法中,JUnit 对输入参数进行处理,首先检查 -m、-v、-wait 等参数,对他们分别进行处理。如果有 -m 参数,将会根据“.”的位置,分割得到 className 和 methodName.

参数一:

首先调用 getTest(),通过 Java 反射实例化 TestSuite:

Class testClass = Class.forName(suiteClassName).asSubclass(TestCase.class); 

new TestSuite(testClass) 

TestSuite 构造函数中,通过调用 Class.getDeclaredMethods(),得到这个类的所有 Public 的方法,当然也包括构造函数,test 开头和非 test 开头的 public 方法。对所有方法进行过滤,仅仅保留 public 并且以“test”开头的方法,本例中为 testPay()testPayWithDiscount()。然后分别调用 TestSuite 的 createTest() 为每个方法生成一个实例:

theClass.getConstructor(String.class).newInstance(new Object[0]);

并且都保存在 Vector<Test> fTests 中。

参数二:

与方法一不同的的是,并不通过反射获得相应的方法,因为参数中指定了特定的方法。直接根据输入参数调用 TestSuite 的 createTest(),通过反射直接生成 TestCase 实例。

TestCase 实例的运行

生成 TestCase 实例后,两种参数都将调用 TestRunner 的 doRun() 方法。下面将对第二种参数进行详细介绍,介绍一个 TestCase 实例是怎么运行的,并且怎样与 TestResult 和 TestListener 结合。

doRun() 方法中,实例化 TestResult result, 为 result 加上 Listener (new ResultPrinter()),用来监听 Test 运行中的事件。然后运行 TestResult.Run(test)run() 方法中调用 TestCase 的 runBare()runBare() 会把所有的异常都抛出来,result 将接受到所有的异常。runBare() 首先会运行 setup(),接着运行 runTest(), 最后 tearDown()。回头再看前面的 output,就明白了为什么 setup()tearDown() 会在每个方法运行前和后运行,对于参数二,运行了两次。

TestResult

TestResult 有两个 List,用来记录 Exception 和 Failure。捕获 runBare() 抛出的 Exception,首先判断是否为 AssertionFailedError,是则调用 addFailure() 把,把异常加到 fFailures。否则则并调用 addError() 方法,把异常加到 fErrors 中。

catch (AssertionFailedError e) { 
    addFailure(test, e); 
} 
catch (ThreadDeath e) { // don't catch ThreadDeath by accident 
    throw e; 
} 
catch (Throwable e) { 
    ddError(test, e);
}

TestListener

前面提到 result 加上了一个 ResultPrinter,ResultPrinter 会记录运行中的所有 Exception,并且实时地以不同的格式输出。当所有的 Test 都运行完毕后,ResultPrinter 会对 result 进行分析,首先输出运行的时间,接着 printError() 输出 fErrors 的个数,printFailures() 则输出 fFailures 的个数。PrintFooter() 根据 result.wasSuccessful(),如果成功,则打印 OK 和 test 运行的总次数,如果失败,则打印出 test 总的运行的个数,失败和错误的个数。


参数一的统计输出结果:
				
Time: 0.016 
There was 1 failure: 
1) testPay(TestShoppingCart)junit.framework.AssertionFailedError: 
expected:<30> but FAILURES!!! 
Tests run: 2,  Failures: 1,  Errors: 0 


清单一:

清单二:

完整生命周期

整个生命周期将在下图显示:


图 1. Junit 完整生命周期
图 1. Junit 完整生命周期

总结

通过上面的介绍,本文深入地讲解了 JUnit 的核心类和接口,TestCase 的完整生命周期。掌握了这些,开发者有了更加灵活的自用度,可以根据自己特定的项目的特性,定制最合适自身的 MyTestRunner,MyTestResult,MyTestSuite,MyTestListener。从而提高工作效率,发挥 JUnit 的最大作用。

本文出处:http://www.ibm.com/developerworks/cn/java/j-lo-junit-intro/index.html

分享到:
评论

相关推荐

    junit5.jar

    同时,JUnit Jupiter还提供了参数化测试、条件测试、生命周期方法等功能,满足了复杂测试场景的需求。 JUnit Vintage则保留了对JUnit 4测试的支持,这意味着在JUnit 5环境中可以无缝运行JUnit 4编写的测试,确保了...

    JUnit5 User Guide

    这些特性包括注解的改进、断言的增强、假设的使用、测试禁用、条件测试执行、标签和过滤、测试实例生命周期管理、嵌套测试、构造函数和方法的依赖注入、测试接口和默认方法、重复测试、参数化测试、消费参数、测试...

    JUnit设计模式分析及简化的JUnit代码

    观察者模式在JUnit中也得到了广泛应用,比如`TestWatcher`,它监听测试的生命周期事件,如测试开始、结束、失败或成功。开发者可以通过扩展这个类来定制测试事件的通知行为。 此外,JUnit还利用了装饰者模式,例如...

    Junit5.jar包,代码测试工具

    - `TestInstance.Lifecycle.PER_CLASS`生命周期选项允许在类级别而非方法级别创建测试实例,提高性能。 3. **异步测试**: - `@Async`注解允许声明测试方法为异步,测试框架会等待异步操作完成后再进行断言。 4....

    junit所需包

    还有`jupiter-engine.jar`,这是JUnit 5的主要执行引擎,提供了新的注解和测试生命周期特性。 当缺少这些依赖时,就可能出现描述中的“initializationError”。例如,如果你只引入了`junit.jar`而没有引入`hamcrest...

    JUnit入门及简单使用

    3. 测试生命周期方法:如`setUp()`和`tearDown()`,分别在每个测试方法执行前和执行后调用,用于初始化和清理测试环境。 4. 图形和文本界面的测试运行器:方便用户查看测试结果。 5. 框架继承:在JUnit中,通常需要...

    junit4.13.zip

    还有`@Before`和`@After`注解,分别用于定义在每个测试方法之前和之后执行的代码,以及`@BeforeClass`和`@AfterClass`,用于在整个测试类的生命周期中只执行一次的方法。 2. `hamcrest-2.2.jar`:Hamcrest是一个...

    junit5-r5.5.2.zip

    - **JUnit Jupiter**:主要负责测试注解、断言、测试生命周期和扩展点的实现,它是编写新式JUnit 5测试的核心。 - **JUnit Vintage**:允许在JUnit 5环境中运行JUnit 3和JUnit 4的测试。 3. **新特性** - **注解...

    junit实战第二版

    JUnit的生命周期涉及测试的初始化、执行以及清理等过程,了解这些过程对于编写能够正确反映软件行为的测试至关重要。 整本书籍不仅仅是对JUnit框架本身的介绍,也是对单元测试实践的指导。它强调测试的编写应该符合...

    junit4测试jar包

    8. **规则(Rules)**:JUnit4引入了规则(Rules)的概念,允许自定义测试行为,如使用`@Rule`注解配合`ExternalResource`实现资源的生命周期管理。 9. **假设(Assumptions)**:`Assume`类提供了方法,可以在测试...

    JUnit API和入门手册 chm

    JUnit 5的出现带来了更多的新特性,如Lambda表达式支持、条件测试、测试生命周期控制等,使得单元测试更加灵活和强大。 总的来说,掌握JUnit对于任何Java开发者来说都是必备技能,通过阅读这些资源,你可以从基础到...

    JUnit4JUnit4JUnit4(文档)

    还有 `@BeforeClass` 和 `@AfterClass` 用于在整个测试类的生命周期内只执行一次的初始化和清理操作。 2. **断言**: JUnit4 提供了多种断言方法,如 `assertEquals()`、`assertTrue()`、`assertFalse()` 等,用于...

    Junit设计模式分析(带源码)

    - 模板方法模式:JUnit的Test接口定义了测试的生命周期,具体实现由子类覆盖模板方法完成,如setup和tearDown。 - 钩子函数:@Before、@After等注解体现了观察者模式,它们在特定事件(如测试开始或结束)触发时...

    junit4教程(《Junit4初探》)

    - `@BeforeClass`: 仅在测试类的生命周期中运行一次,常用于一次性设置全局环境。 - `@AfterClass`: 仅在测试类的生命周期结束时运行一次,用于清理全局资源。 ### 2. 断言 JUnit4提供了丰富的断言方法,如`...

    junit-4.9官方下载

    通过查看这个JAR中的文档,开发者可以了解到每个类、接口、方法的功能、参数、返回值以及可能抛出的异常,有助于他们编写正确的测试代码。 3. **junit-4.9-sources.jar**:提供了JUnit 4.9的源代码,这对于开发者来...

    Junit4的解释文档javadoc下载

    在JavaDoc中,你可以找到关于JUnit4的所有公共类、接口和方法的详细信息。例如,`@Test`注解是JUnit4的核心,用于标记测试方法。这个注解告诉JUnit这个方法是一个测试,应该在执行测试套件时被调用。JavaDoc会解释...

    JUnit-4.10.jar

    例如,你可以创建自己的规则来控制测试执行的生命周期,如定时器、日志记录或资源管理。 在实际应用中,JUnit-4.10.jar通常与其他工具结合使用,例如Eclipse或IntelliJ IDEA这样的集成开发环境,它们提供了对JUnit...

    Junit入门及应用

    2. TestCase 抽象类:实现了 Test 接口,并提供了如 setUp() 和 tearDown() 这样的生命周期方法。setUp() 用于在每个测试方法执行前设置测试环境,tearDown() 用于在测试结束后清理资源。 JUnit 的设计理念是简化...

    Junit 5 文档

    8. 测试实例生命周期(TestInstanceLifecycle):JUnit 5对测试实例的生命周期提供了更多控制,比如可以通过@DirtiesContext注解标记测试方法执行后需要重置测试上下文。 9. 嵌套测试(NestedTests):JUnit 5支持...

    junit5-r5.3.0

    13. **测试配置**:通过`@TestInstance`注解,可以控制测试实例的生命周期,选择在每个测试方法之前还是每个测试类之前创建实例。 14. **测试报告**:JUnit 5提供了一种扩展机制,可以自定义测试报告的格式和内容。...

Global site tag (gtag.js) - Google Analytics