JUnit是由 Erich Gamma 和 Kent Beck 编写的一个回归测试框架(regression testing framework),供Java开发人员编写单元测试之用。

1、概述
  Junit测试是程序员测试,即所谓白盒测试,因为程序员知道被测试的软件如何(How)完成功能和完成什么样(What)的功能。
  Junit本质上是一套框架,即开发者制定了一套条条框框,遵循这此条条框框要求编写测试代码,如继承某个类,实现某个接口,就可以用Junit进行自动测试了。
  由于Junit相对独立于所编写的代码,可以测试代码的编写可以先于实现代码的编写,XP 中推崇的 test first
design的实现有了现成的手段:用Junit写测试代码,写实现代码,运行测试,测试失败,修改实现代码,再运行测试,直到测试成功。以后对代码的修
改和优化,运行测试成功,则修改成功。
  Java 下的 team 开发,采用 cvs(版本控制) + ant(项目管理) + junit(集成测试) 的模式时,通过对ant的配置,可以很简单地实现测试自动化。

  对不同性质的被测对象,如Class,Jsp,Servlet,Ejb等,Junit有不同的使用技巧,以后慢慢地分别讲叙。以下以Class测试为例讲解,除非特殊说明。

2、下载安装

去Junit主页下载最新版本3.8.1程序包junit-3.8.1.zip

用winzip或unzip将junit-3.8.1.zip解压缩到某一目录名为$JUNITHOME

将junit.jar和$JUNITHOME/junit加入到CLASSPATH中,加入后者只因为测试例程在那个目录下。

注意不要将junit.jar放在jdk的extension目录下

运行命令,结果如右图。
java junit.swingui.TestRunner junit.samples.AllTests

3、Junit架构
  下面以Money这个类为例进行说明。

public class Money {
private int fAmount;//余额
private String fCurrency;//货币类型

public Money(int amount, String currency) {
fAmount= amount;
fCurrency= currency;
}

public int amount() {
return fAmount;
}

public String currency() {
return fCurrency;
}

public Money add(Money m) {//加钱
return new Money(amount()+m.amount(), currency());
}

public boolean equals(Object anObject) {//判断钱数是否相等
if (anObject instanceof Money) {
Money aMoney= (Money)anObject;
return aMoney.currency().equals(currency())
&& amount() == aMoney.amount();
}
return false;
}
}

  Junit本身是围绕着两个设计模式来设计的:命令模式和集成模式.

命令模式
  利用TestCase定义一个子类,在这个子类中生成一个被测试的对象,编写代码检测某个方法被调用后对象的状态与预期的状态是否一致,进而断言程序代码有没有bug。
  当这个子类要测试不只一个方法的实现代码时,可以先建立测试基础,让这些测试在同一个基础上运行,一方面可以减少每个测试的初始化,而且可以测试这些不同方法之间的联系。
  例如,我们要测试Money的Add方法,可以如下:
public class MoneyTest extends TestCase { //TestCase的子类
public void testAdd() { //把测试代码放在testAdd中
Money m12CHF= new Money(12, "CHF"); //本行和下一行进行一些初始化
Money m14CHF= new Money(14, "CHF");
Money expected= new Money(26, "CHF");//预期的结果
Money result= m12CHF.add(m14CHF); //运行被测试的方法
Assert.assertTrue(expected.equals(result)); //判断运行结果是否与预期的相同
}
}

  如果测试一下equals方法,用类似的代码,如下:
public class MoneyTest extends TestCase { //TestCase的子类
public void testEquals() { //把测试代码放在testEquals中
Money m12CHF= new Money(12, "CHF"); //本行和下一行进行一些初始化
Money m14CHF= new Money(14, "CHF");

Assert.assertTrue(!m12CHF.equals(null));//进行不同情况的测试
Assert.assertEquals(m12CHF, m12CHF);
Assert.assertEquals(m12CHF, new Money(12, "CHF")); // (1)
Assert.assertTrue(!m12CHF.equals(m14CHF));
}
}

  当要同时进行测试Add和equals方法时,可以将它们的各自的初始化工作,合并到一起进行,形成测试基础,用setUp初始化,用tearDown清除。如下:
public class MoneyTest extends TestCase {//TestCase的子类
private Money f12CHF;//提取公用的对象
private Money f14CHF;

protected void setUp() {//初始化公用对象
f12CHF= new Money(12, "CHF");
f14CHF= new Money(14, "CHF");
}
public void testEquals() {//测试equals方法的正确性
Assert.assertTrue(!f12CHF.equals(null));
Assert.assertEquals(f12CHF, f12CHF);
Assert.assertEquals(f12CHF, new Money(12, "CHF"));
Assert.assertTrue(!f12CHF.equals(f14CHF));
}

public void testSimpleAdd() {//测试add方法的正确性
Money expected= new Money(26, "CHF");
Money result= f12CHF.add(f14CHF);
Assert.assertTrue(expected.equals(result));
}
}

  将以上三个中的任一个TestCase子类代码保存到名为MoneyTest.java的文件里,并在文件首行增加
import junit.framework.*;
,都是可以运行的。关于Junit运行的问题很有意思,下面单独说明。
  上面为解释概念“测试基础(fixture)”,引入了两个对两个方法的测试。命令模式与集成模式的本质区别是,前者一次只运行一个测试。

集成模式
  利用TestSuite可以将一个TestCase子类中所有test***()方法包含进来一起运行,还可将TestSuite子类也包含进来,从
而行成了一种等级关系。可以把TestSuite视为一个容器,可以盛放TestCase中的test***()方法,它自己也可以嵌套。这种体系架构,
非常类似于现实中程序一步步开发一步步集成的现况。
  对上面的例子,有代码如下:
public class MoneyTest extends TestCase {//TestCase的子类
....
public static Test suite() {//静态Test
TestSuite suite= new TestSuite();//生成一个TestSuite
suite.addTest(new MoneyTest("testEquals")); //加入测试方法
suite.addTest(new MoneyTest("testSimpleAdd"));
return suite;
}
}

  从Junit2.0开始,有列简捷的方法:
public class MoneyTest extends TestCase {//TestCase的子类
....
public static Test suite() {静态Test
return new TestSuite(MoneyTest.class); //以类为参数
}
}

  TestSuite见嵌套的例子,在后面应用案例中有。
  

4、测试代码的运行
  先说最常用的集成模式。
  测试代码写好以后,可以相应的类中写main方法,用java命令直接运行;也可以不写main方法,用Junit提供的运行器运行。Junit提供了textui,awtui和swingui三种运行器。
  以前面第2步中的AllTests运行为例,可有四种:

java junit.textui.TestRunner junit.samples.AllTests
java junit.awtui.TestRunner junit.samples.AllTests
java junit.swingui.TestRunner junit.samples.AllTests
java junit.samples.AllTests

  main方法中一般也都是简单地用Runner调用suite(),当没有main时,TestRunner自己以运行的类为参数生成了一个TestSuite.
  
  对于命令模式的运行,有两种方法。

静态方法

TestCase test= new MoneyTest("simple add") {
public void runTest() {
testSimpleAdd();
}
};

动态方法

TestCase test= new MoneyTest("testSimpleAdd");

  我试了一下,好象有问题,哪位朋友成功了,请指点我一下。确实可以。

import junit.framework.*;

public class MoneyTest extends TestCase {//TestCase的子类
private Money f12CHF;//提取公用的对象
private Money f14CHF;
public MoneyTest(String name){
super(name);
}
protected void setUp() {//初始化公用对象
f12CHF= new Money(12, "CHF");
f14CHF= new Money(14, "CHF");
}
public void testEquals() {//测试equals方法的正确性
Assert.assertTrue(!f12CHF.equals(null));
Assert.assertEquals(f12CHF, f12CHF);
Assert.assertEquals(f12CHF, new Money(12, "CHF"));
Assert.assertTrue(!f12CHF.equals(f14CHF));
}

public void testAdd() {//测试add方法的正确性
Money expected= new Money(26, "CHF");
Money result= f12CHF.add(f14CHF);
Assert.assertTrue(expected.equals(result));
}
// public static void main(String[] args) {
// TestCase test=new MoneyTest("simple add") {
// public void runTest() {
// testAdd();
// }
// };
// junit.textui.TestRunner.run(test);
// }
public static void main(String[] args) {
TestCase test=new MoneyTest("testAdd");
junit.textui.TestRunner.run(test);
}
}

再给一个静态方法用集成测试的例子:
public static Test suite() {
TestSuite suite= new TestSuite();
suite.addTest(
new testCar("getWheels") {
protected void runTest() { testGetWheels(); }
}
);

suite.addTest(
new testCar("getSeats") {
protected void runTest() { testGetSeats(); }
}
);
return suite;
}

5、应用案例

Junit Primer例程,运行如下:
java com.hedong.JunitLearning.Primer.ShoppingCartTest

Ant+Junit+Mailto实现自动编译、调试并发送结果的build.xml

JUnit实施,写得很棒,理解也深刻。例程运行如下:
java com.hedong.JunitLearning.car.testCarNoJunit
java junit.swingui.TestRunner com.hedong.JunitLearning.car.testCar

Junit与log4j结合,阿菜的例程运行:
cd acai
ant junit

6、一些问题
  有人在实践基础上总结出一些非常有价值的使用技巧,我没有经过一一“测试”,暂列在此。

不要用TestCase的构造函数初始化Fixture,而要用setUp()和tearDown()方法。

不要依赖或假定测试运行的顺序,因为JUnit利用Vector保存测试方法。所以不同的平台会按不同的顺序从Vector中取出测试方法。不知3.8中是不是还是如此,不过它提供的例子有一个是指定用VectorSuite的,如果不指定呢?

避免编写有副作用的TestCase。例如:如果随后的测试依赖于某些特定的交易数据,就不要提交交易数据。简单的回滚就可以了。

当继承一个测试类时,记得调用父类的setUp()和tearDown()方法。

将测试代码和工作代码放在一起,一边同步编译和更新。(使用Ant中有支持junit的task.)

测试类和测试方法应该有一致的命名方案。如在工作类名前加上test从而形成测试类名。

确保测试与时间无关,不要依赖使用过期的数据进行测试。导致在随后的维护过程中很难重现测试。

如果你编写的软件面向国际市场,编写测试时要考虑国际化的因素。不要仅用母语的Locale进行测试。

尽可能地利用JUnit提供地assert/fail方法以及异常处理的方法,可以使代码更为简洁。

测试要尽可能地小,执行速度快。

把测试程序建立在与被测对象相同的包中

在你的原始代码目录中避免测试码出现,可在一个源码镜像目录中放测试码

在自己的应用程序包中包含一个TestSuite测试类

7、相关资源下载
以下jar包,我只是做了打包、编译和调试的工作,供下载学习之用,相关的权利属于原作者。

可运行例程.jar

Build.xml

阿菜的例程

Junit API 汉译(pdf)

8、未完成的任务

httpunit

cactus

将Junit用链接池测试

java单元测试(使用junit)的更多相关文章

  1. Java单元测试框架 JUnit

    Java单元测试框架 JUnit JUnit是一个Java语言的单元测试框架.它由Kent Beck和Erich Gamma建立,逐渐成为源于KentBeck的sUnit的xUnit家族中为最成功的一 ...

  2. Maven的安装配置及初次创建项目与java单元测试工具JUnit

    Maven  安装     1.把maven安装包解压到某个位置     2.配置M2_HOME环境变量指向这个位置 3.在path环境变量中添加;%M2_HOME%\bin 配置镜像 国内的阿里云镜 ...

  3. java单元测试(Junit)

    JUnit是由 Erich Gamma 和 Kent Beck 编写的一个回归测试框架(regression testing framework),供Java开发人员编写单元测试之用. 对不同性质的被 ...

  4. Java单元测试之JUnit 5快速上手

    前言 单元测试是软件开发中必不可少的一环,但是在平常开发中往往因为项目周期紧,工作量大而被选择忽略,这样往往导致软件问题层出不穷.线上出现的不少问题其实在有单元测试的情况下就可以及时发现和处理,因此培 ...

  5. Java知识积累——单元测试和JUnit(一)

    说起单元测试,刚毕业或者没毕业的人可能大多停留在课本讲述的定义阶段,至于具体是怎么定义的,估计也不会有太多人记得.我们的教育总是这样让人“欣 慰”.那么什么是单元测试呢?具体科学的定义咱就不去关心了, ...

  6. Java单元测试(Junit+Mock+代码覆盖率)

    微信公众号[程序员江湖] 作者黄小斜,斜杠青年,某985硕士,阿里 Java 研发工程师,于 2018 年秋招拿到 BAT 头条.网易.滴滴等 8 个大厂 offer,目前致力于分享这几年的学习经验. ...

  7. 使用Junit进行Java单元测试

    1.新建一个Number类,该类中包含两个函数,求和.求差 2.在eclipse上安装Junit 右键test工程,选择“Properties”→“Java Build Path”→“Librarie ...

  8. Java单元测试(Junit+Mock+代码覆盖率)---------转

    Java单元测试(Junit+Mock+代码覆盖率) 原文见此处 单元测试是编写测试代码,用来检测特定的.明确的.细颗粒的功能.单元测试并不一定保证程序功能是正确的,更不保证整体业务是准备的. 单元测 ...

  9. JUnit java单元测试

    首先须导入JUnit包:所在项目右击->Build Path->Add Libraries->选择JUnit->选择一个版本->Finish 一.手动生成 1.测试方法, ...

  10. Java单元测试技术1

    另外两篇关于介绍easemock的文章:EasyMock 使用方法与原理剖析,使用 EasyMock 更轻松地进行测试 摘要:本文针对当前业软开发现状,先分析了WEB开发的技术特点和单元测试要解决的问 ...

随机推荐

  1. 转字符驱动实例gpio

    概述: 字符设备驱动程序: 是按照字符设备要求完成的由操作系统调用的代码. 重点理解以下内容:  1. 驱动是写给操作系统的代码,它不是直接给用户层程序调用的,而是给系统调用的  2. 所以驱动要向系 ...

  2. 从零开始学ios开发(十一):Tab Bars和Pickers

    不好意思各位,本人休息了一个礼拜,所以这次的进度延后了,而且这次的学习的内容比较多,时间用的也比较长,文章发布的时间间隔有些长了,望各位谅解,下面继续我们的ios之旅. 这次我们主要学习的内容有2个, ...

  3. javascript之流程控制 和函数的容易忽略点

    1.流程控制 1> for in  仅用于 对象的遍历: var box={ "name":'小红', 'age':18, 'height':165 }; for(var b ...

  4. 送给那些喜欢myeclipse黑色主题但是又不知道怎么配色的人

    设置MyEclipse黑色主题背景 1. 下载 http://eclipsecolorthemes.org/  看哪个合适直接点击进入, 下载右边的epf 2. 下载完成...打开myeclipse. ...

  5. python 基于小顶堆实现随机抽样

    起因:之前用蓄水池抽样,算法精简,但直观性很差. 所以这次采用了简单的,为没一个行,赋值一个随机值,然后取 最大的K个作为,随机样本. 基本思路:为每一个行(record,记录,实体) 赋一个rand ...

  6. SQL SERVER字符串函数

    本篇文章还是学习<程序员的SQL金典>内容的记录,此次将讲解的是SQL SERVER的字符串函数. 其实数据库跟程序语言库一样,都会集成很多可以使用的API.这些API,如果你熟悉的话,将 ...

  7. sysconf和pathconf使用

    问题描述:          查看系统运行时的限制值 问题解决: 执行效果: 源代码:

  8. sql查询结果本身要被使用两次

    一.问题 查询用户所有的错题数目到前端展示,要求展示的时候要有错题的编号,从1开始递增.如果删除了第5题,则将后面的题编号均向前挪. 二.分析 错题是在用户每次做题过程中插入到错题表中的,或者将题目推 ...

  9. SpringMVC数据绑定全面示例(复杂对象,数组等)

    点击链接查询原文 http://www.xdemo.org/springmvc-data-bind/ 已经使用SpringMVC开发了几个项目,平时也有不少朋友问我数据怎么传输,怎么绑定之类的话题,今 ...

  10. Android串口通信(基于Tiny6410平台)

    友善之臂的Android系统有他们自己编写的一个串口通信程序,网上没有找到他的源代码,而且界面操作不在一个界面,不是很方便,这里我自己写了一个粗糙点的串口通信程序. 同样这里还是调用友善之臂的frie ...