单元测试一直以来都是让程序员又爱又恨的家伙。一方面它可以给我们提供最细粒度的单元测试,保障我们代码重构时的质量。另一方面它会增加我们的工作量,可以说一个不完整的单元测试就跟没有测试差不多。而且要写一个有效的单元测试,除了要花费大量时间之外,更加需要丰富的单元测试经验和编码经验。
而且最重要的是,我们其实不怎么了解单元测试的核心——JUnit这个工具。
俗话说,工欲善其事,必先利其器。今天开始我们就来了解一下这个工具。看了下经典的Junit In Action,已经是很古老的版本。自从Junit发布到4.1版本以后,单元测试的写法就已经有了划时代的变化,大量使用标记以及基础框架更加灵活和可定制,已经成为Junit的特点。虽然本人很久以前曾经整理过Junit4.1的学习笔记,不过很不幸由于本人自己的疏忽已经不知去向,无奈之下只好重新整理最新版本4.7的学习笔记,并且借此机会跟大家分享一下。
因为这是学习笔记,所以更多会注重代码的分析和一些应用点评,所以不是具体的使用教学,如果大家想问这方面的问题可以单独和我联系,我会把我的相关经验都和大家分享。
首先我们来分析一下JUnit4.7的核心。我们从测试的入口开始去了解Junit是怎么去跑测试的。从调试模式中我们发现以下的类会被涉及到org.junit.runner.JUnitCore这个类。
JUnitCore就是Junit的入口,执行Junit跑测试的就是它。它里面提供了各种跑测试的方法,其中实际使用的方法包括
public Result run(Class<?>... classes)
public Result run(Computer computer, Class<?>... classes)
public Result run(Request request)
public Result run(junit.framework.Test test)
其中前2个方法都会调用第三个方法run(Request request),而最后一个方法用于执行JUnit3.8风格的测试用例。
那么,来看一下Request这个抽象类。我们发现,大部分的测试都会被封装为一个具体的Request,而由Request根据类的类型来决定应该用什么Runner来执行这组测试。其中关键的一个方法就是:
public abstract Runner getRunner();
除了这个方法以外,Request还提供很多帮助我们把一个东西封装为Request的方法:
public static Request method(Class<?> clazz, String methodName)
public static Request aClass(Class<?> clazz)
public static Request classWithoutSuiteMethod(Class<?> clazz)
public static Request classes(Computer computer, Class<?>... classes)
public static Request classes(Class<?>... classes)
public static Request errorReport(Class<?> klass, Throwable cause)
public static Request runner(final Runner runner)
以方便我们使用。
那么Request是怎么知道我们需要用什么Runner来跑测试的呢?Runner又是用来做什么工作的呢?从上面的封装方法中,我们查看classes方法,发现里面做了这些事:
public static Request classes(Computer computer, Class<?>... classes) {
try {
AllDefaultPossibilitiesBuilder builder= new AllDefaultPossibilitiesBuilder(true);
Runner suite= computer.getSuite(builder, classes);
return runner(suite);
} catch (InitializationError e) {
throw new RuntimeException(
"Bug in saff's brain: Suite constructor, called as above, should always complete");
}
}
好,留意到这个叫AllDefaultPossibilitiesBuilder的类,查看它的代码发现它的一个叫runnerForClass的方法让我很在意:
public Runner runnerForClass(Class<?> testClass) throws Throwable {
List<RunnerBuilder> builders= Arrays.asList(
ignoredBuilder(),
annotatedBuilder(),
suiteMethodBuilder(),
junit3Builder(),
junit4Builder());
for (RunnerBuilder each : builders) {
Runner runner= each.safeRunnerForClass(testClass);
if (runner != null)
return runner;
}
return null;
}
原来,Request决定应该用什么Runner这个工作是交由RunnerBuilder决定的,而默认的RunnerBuilder就是这个AllDefaultPossibilitiesBuilder。它在里面定义了所有的默认RunnerBuilder列表,然后通过逐个调用RunnerBuilder.safeRunnerForClass方法来看当前的测试类是否适用这个RunnerBuilder所构建的Runner。由于junit4BUilder是最后一个判断的Builder,也说明它是默认的Builder,那么我们就来看看这个方法具体做了些什么:
protected JUnit4Builder junit4Builder() {
return new JUnit4Builder();
}
然后看JUnit4Builder这个类的safeRunnerForClass方法,发现这个方法默认是从RunnerBuilder这个抽象类中继承下来的,而其中调用的runnerForClass方法由子类实现,具体的代码为:
public Runner runnerForClass(Class<?> testClass) throws Throwable {
return new BlockJUnit4ClassRunner(testClass);
}
然后我顺藤摸瓜,从BlockJUnit4ClassRunner的构造函数发现具体调用的ParentRunner的构造函数:
protected ParentRunner(Class<?> testClass) throws InitializationError {
fTestClass= new TestClass(testClass);
validate();
}
然后再看到TestClass的构造函数如下:
public TestClass(Class<?> klass) {
fClass= klass;
if (klass != null && klass.getConstructors().length > 1)
throw new IllegalArgumentException(
"Test class can only have one constructor");
......
}
好了,我们发现判断是否使用BlockJunit4ClassRunner的标准,首先当然就是它不符合其它Builder类型的构造另外我们发现只要它的类的构造函数为1就可以了,也就是测试类只允许有一个构造函数。
在按这个道理分析一下junit3Builder,发现其判别条件为:
package org.junit.internal.builders;
import org.junit.internal.runners.JUnit38ClassRunner;
import org.junit.runner.Runner;
import org.junit.runners.model.RunnerBuilder;
public class JUnit3Builder extends RunnerBuilder {
@Override
public Runner runnerForClass(Class<?> testClass) throws Throwable {
if (isPre4Test(testClass))
return new JUnit38ClassRunner(testClass);
return null;
}
boolean isPre4Test(Class<?> testClass) {
return junit.framework.TestCase.class.isAssignableFrom(testClass);
}
}
再看JUnit38ClassRunner的构造函数:
public JUnit38ClassRunner(Class<?> klass) {
this(new TestSuite(klass.asSubclass(TestCase.class)));
}
先会判断它是否继承了TestCase,然后会按照以前的要求来判断是否符合测试用例的规格。如果都符合了,就会采用这个Runner来跑测试。
所以,现在的测试用例,不需要像以前教科书说的那样,需要继承TestCase来实现了,而只需要是一个Class就好。以下就是一个具体的测试用例例子:
public class Junit4LikeTestCase
{
//test methods
....
}
就可以了!!而且先透露一下下一节的内容,其实一个测试方法,只要这样写就OK:
@Test public void test(){
//Test Method Block
...
}
好了,这样我们就可以知道整个框架的核心概念:
类名 | 意义 |
org.junit.runner.JUnitCore | 跑测试的前端程序,JUnit的入口 |
org.junit.runner.Request | 用于封装测试请求,并且匹配具体请求和Runner |
org.junit.runner.Runner | 具体测试执行者 |
org.junit.runner.RunnerBuilder | 再Request中根据测试类生成对应Runner的构造器 |
以上的列表就说明了整个Junit中的核心组件。后面我们会围绕后三个核心进行深入的剖析,也就是这个学习笔记系列的主要内容。
分享到:
相关推荐
Junit4.7是Java编程领域中一个非常重要的单元测试框架,它允许开发者对他们的代码进行自动化测试,确保代码的功能正确性。这个完整的jar包包含了Junit4.7的所有类和方法,使得开发者可以直接在项目中引用,进行单元...
JUnit 4.7 是一个广泛使用的Java编程语言的单元测试框架。这个版本是JUnit系列中的一个重要里程碑,它为开发者提供了一套强大而灵活的工具,用于编写和执行测试用例,确保代码的质量和稳定性。在Java开发过程中,...
JUnit的核心概念是测试用例(Test Case),它是测试特定功能或方法的一组相关断言(Assertion)。在JUnit 4.7中,你可以通过创建继承自`org.junit.TestCase`的类来编写测试用例,或者使用注解(Annotation)来简化这...
让我们深入了解一下JUnit 4.7的核心功能和特性。 1. **注解驱动测试**: JUnit 4.7 引入了注解(Annotation)的概念,这使得编写测试用例变得更加简洁。例如,`@Test`注解标记测试方法,`@Before`和`@After`注解...
JUnit 4.7作为早期版本4.x系列的一部分,引入了许多改进和新特性。 2. **主要特性** - **注解(Annotations)**:JUnit 4.7引入了注解,如`@Test`、`@Before`、`@After`等,用于标记测试方法和设置方法,使得测试...
这里我们关注的是JUnit 4.7版本,它是一个重要的里程碑,引入了许多新特性和改进。 首先,JUnit 4.7是JUnit系列的一个升级版,它在JUnit 4的基础上进行了增强。JUnit 4相较于之前的版本,最大的改变是引入了注解...
本文将深入探讨JUnit 4.7的核心特性,帮助开发者更好地利用这一强大的测试框架。 首先,JUnit 4.7引入了注解(Annotation)的广泛使用,这是对传统基于XML配置的一大改进。通过注解,我们可以直接在测试类和方法上...
文件名`junit4.7-SNAPSHOT-20090511-2347`可能指的是JUnit 4.7的一个快照版本,其中`SNAPSHOT`通常表示这是一个开发中的不稳定版本,`20090511-2347`可能是该版本的构建日期和时间。在实际开发中,为了确保稳定性,...
JUnit 4.7 是一个流行的开源测试框架,主要用于编写和执行Java程序的单元测试。它在软件开发过程中扮演着至关重要的角色,确保代码的质量和稳定性。这个版本的JUnit是在JUnit 4系列的一个更新,带来了许多改进和新...
JUnit 4.7是该框架的一个版本,包含了对之前版本的改进和新功能。 在Java开发中,单元测试是对单个或小部分代码进行的功能验证,通常针对方法。通过单元测试,开发者可以确保代码的正确性,降低bug的出现,并且在...
JUnit 4.7 是 JUnit 系列的一个版本,它在前一个版本的基础上引入了一些新特性,以提高测试的灵活性和可读性。这个版本发布于2008年,对于当时的Java开发者来说,它提供了一个更强大的工具来确保代码的质量。 **...
首先,JUnit 4.8.2是JUnit系列的一个稳定版本,相较于早期的4.7版本,它可能包含了修复的bug、性能改进和新的特性。在JUnit 4.x系列中,一个显著的改变是引入了注解(Annotations),这使得编写测试代码更加简洁和...
`junit-4.7.jar`是JUnit 4.7版本的库文件,这个版本发布于2008年,是JUnit系列的一个重要里程碑。在Java开发中,单元测试是验证代码功能正确性的重要手段,它确保了每个独立的代码模块都能正常工作,为软件质量提供...
JUnit 4.7 是JUnit系列的一个版本,它在早期版本的基础上引入了多项改进和新特性。这些改进包括: 1. **注解驱动**:JUnit 4.7 引入了注解(@Test, @Before, @After, @BeforeClass, @AfterClass)来标记测试方法、...
这个名为"junit4.7-SNAPSHOT-20090511-2347.rar"的压缩包文件包含了JUnit 4.7的一个快照版本,发布日期为2009年5月11日,具体时间是23:47。这个版本可能是一个开发过程中的不稳定版本,"SNAPSHOT"通常表示这是一个...
深圳电信培训中心徐海蛟老师上课用的junit4.7 api chm 速查中文手册.吐血奉献o(∩_∩)o...哈哈
junit-4.7.jar 用于java测试的包
junit-4.7-src.jar junit-4.7-src.jar junit-4.7-src.jar junit-4.7-src.jar junit-4.7-src.jar junit-4.7-src.jar junit-4.7-src.jar
本文档主要介绍了如何将Struts2、Spring3.0、JUnit4.7和Maven2.2.1这几种技术框架整合在一起,以构建一个功能完善的Java EE项目。该文档的目标是帮助开发人员理解如何在实际项目中应用这些框架,并确保它们能够协同...