本文转自http://87nba87.iteye.com/blog/665821
JUnit是*Unit家族的第一产品,一直被广泛应用。他是Kent Beck和Erich Gamma两位大师的作品(GOF中的两人,设计模式那本书是4个人写的,这两个是其中两位)。目前已经成为Java领域单元测试默认的标准。
现在的版本是4.*,由于4的版本基于了JDK5以上,利用了大量的Annotation,是测试变的更加方便。不过本文还是介绍JUnit3.8是如何使用的。关于4的介绍,接下来会马上跟进。
当然JUnit是非常简单,但是做单元测试绰绰有余。
首先先了解一下JUnit的目的。
我们利用JUnit进行单元测试,目的就是发现我们代码中可能存在的问题,当然我们的单元测试不可能发现所有的问题,但是确可以保证我们已知的功能,和我们可以考虑到的功能进行测试。
我们先来一个类,然后根据这个类编写相关的测试用例,将JUnit3.8关键知识点列出。
用户帐户类
UserAccount.java
/**
* 用户帐户对象
*/
public class UserAccount {
private int money;
public UserAccount(){
money=0;
}
/**
* 存钱
*/
public void depositeMoney(int i){
money+=i;
}
/**
* 取钱
*/
public void withdrawMoney(int i){
money-=i;
}
/**
* 查看帐户
*/
public int showMoney(){
return money;
}
}
这个就是我们带测试的一个类,实际状况肯定比这个要复杂的多,这个例子仅仅是引出JUnit,看JUnit如何使用,因为具体的测试用例是相关人员自己构建的。
1:构建TestCase类
public class UserAccountTest extends TestCase{
}
测试用例类必须实现TestCase,命名习惯一般是在待测试类名称后面加上Test。
2:构建测试方法
可以自动执行的测试方法必须满足如下几个条件
1)必须是public
2)无返回值void
3)方法名称以test开始。(一般是在带测试方法前加上test)
4)无任何参数
/**
* 测试存钱>0
*/
public void testDepositeMoney(){
UserAccount account=new UserAccount();
account.depositeMoney(100);
Assert.assertEquals(100, account.showMoney());
}
这里涉及到一个关键的类Assert(断言),断言就是一定是真的。不是真的就结束。
运行一下,看见的是绿色。OK,只要是绿色一切都是正常的。
接下来我们要测试一些用例:
1)存入的钱小于0
2)取出的钱小于0
3)帐户没有钱,直接取
4)帐户有钱,但是钱不够
5)存入足够的钱,然后取出。
6)存入50,取100,存入200,取50,存入30,存入10,取100,取200.在每次存取的时候设置断言。
在我们要实现这些用例的时候我们发现了,我们写的UserAccount存在很多问题。
用例1,2,3,4,这四种明显错误的操作,我们的程序竟然没有报任何的错误。
/**
* 存钱小于0
*/
public void testDepositeMoney2(){
UserAccount account=new UserAccount();
account.depositeMoney(-100);
}
/**
* 取钱数目小于0
*/
public void testWithdrawMoney(){
UserAccount account=new UserAccount();
account.withdrawMoney(-100);
}
/**
* 帐户没有钱,取钱
*/
public void testWithdrawMoney2(){
UserAccount account=new UserAccount();
account.withdrawMoney(100);
}
/**
* 先存钱,在取钱,依然余额不足
*/
public void testWithdrawMoney3(){
UserAccount account=new UserAccount();
account.depositeMoney(50);
account.withdrawMoney(100);
}
以上四个方法,如果我们都调用showMeney的方法会发现,帐户的余额肯定存在小于0的情况,这是明显不对的,我们需要将我们的UserAccount的bug进行修改。
/**
* 用户帐户对象
*/
public class UserAccount {
private int money;
public UserAccount(){
money=0;
}
/**
* 存钱
* @param i
*/
public void depositeMoney(int i) throws Exception{
if(i<0)
throw new Exception("存钱数目必须大于0.");
money+=i;
}
/**
* 取钱
* @param i
* @return
*/
public void withdrawMoney(int i) throws Exception{
if(i<0)
throw new Exception("取钱数目必须大于0.");
if(money<i)
throw new Exception("帐户余额不足.");
money-=i;
}
/**
* 查看帐户
* @return
*/
public int showMoney(){
return money;
}
}
在完善我们的测试用例之前,还需要将setUp和tearDown这两个方法引入进来。
setUp()这个方法是JUnit默认执行的,就是当调用测试方法之前要先运行这个方法。
当测试方法运行结束的时候要运行另一个方法tearDown().
Assert.fail();
这个方法在我们的测试用例中大量出现,这个方法的意思就是,当成与运行这条语句的时候结束所有的断言。
完整的测试类:
UserAccountTest.java
public class UserAccountTest extends TestCase{
private UserAccount account;
/**
* 任何一个测试方法执行前,必然运行的方法
*/
protected void setUp(){
account=new UserAccount();
}
/**
* 任何一个测试方法执行后,必然运行的方法
*/
protected void tearDown(){
}
/**
* 测试存钱>0
*/
public void testDepositeMoney(){
try {
account.depositeMoney(100);
} catch (Exception e) {
Assert.fail();
}
Assert.assertEquals(100, account.showMoney());
}
/**
* 测试存钱小于0
*/
public void testDepositeMoney2(){
try {
account.depositeMoney(-100);
Assert.fail();
} catch (Exception e) {
Assert.assertEquals("存钱数目必须大于0.", e.getMessage());
}
}
/**
* 测试取钱数目小于0
*/
public void testWithdrawMoney(){
try {
account.withdrawMoney(-100);
Assert.fail();
} catch (Exception e) {
Assert.assertEquals("取钱数目必须大于0.", e.getMessage());
}
}
/**
* 测试帐户没有钱,取钱
*/
public void testWithdrawMoney2(){
try {
account.withdrawMoney(100);
Assert.fail();
} catch (Exception e) {
Assert.assertEquals("帐户余额不足.", e.getMessage());
}
}
/**
* 先存钱,在取钱,依然余额不足
*/
public void testWithdrawMoney3(){
try {
account.depositeMoney(50);
account.withdrawMoney(100);
Assert.fail();
} catch (Exception e) {
Assert.assertEquals("帐户余额不足.", e.getMessage());
}
}
/**
* 先存钱,在取钱
*/
public void testWithdrawMoney4(){
try {
account.depositeMoney(200);
account.withdrawMoney(100);
Assert.assertEquals(100, account.showMoney());
} catch (Exception e) {
e.printStackTrace();
Assert.fail();
}
}
/**
* 测试混合方法
* 存入50,取100,存入200,取50,存入30,存入10,取100,取200.在每次存取的时候设置断言。
*/
public void testComplex(){
try{
account.depositeMoney(50);
Assert.assertEquals(50,account.showMoney());
}
catch (Exception e) {
Assert.fail();
}
try{
account.withdrawMoney(100);
Assert.fail();
}
catch (Exception e) {
Assert.assertEquals("帐户余额不足.", e.getMessage());
}
try{
account.depositeMoney(200);
Assert.assertEquals(250,account.showMoney());
}
catch (Exception e) {
Assert.fail();
}
try{
account.withdrawMoney(50);
Assert.assertEquals(200,account.showMoney());
}
catch (Exception e) {
Assert.fail();
}
try{
account.depositeMoney(30);
Assert.assertEquals(230,account.showMoney());
}
catch (Exception e) {
Assert.fail();
}
try{
account.depositeMoney(10);
Assert.assertEquals(240,account.showMoney());
}
catch (Exception e) {
Assert.fail();
}
try{
account.withdrawMoney(100);
Assert.assertEquals(140,account.showMoney());
}
catch (Exception e) {
Assert.fail();
}
try{
account.withdrawMoney(200);
Assert.fail();
}
catch (Exception e) {
Assert.assertEquals("帐户余额不足.", e.getMessage());
}
Assert.assertEquals(140, account.showMoney());
}
}
这样JUnit的单元测试的方法,和测试基本关键点已经描述,主要还需要在实际的工作中体会使用。
三种测试环境:
接下来是介绍JUnit可以有三种自定义的环境下运行JUnit的测试。
Text,AWT,Swing。
在main方法中运行如下三段代码。
junit.awtui.TestRunner.run(UserAccountTest.class);
junit.swingui.TestRunner.run(UserAccountTest.class);
junit.textui.TestRunner.run(UserAccountTest.class);
会弹出两个窗口,同时在控制台有信息打出。
弹出的两个窗口是JUnit自带的AWT和Swing两种显示环境。
控制台是Text显示环境。
以上是通过main方法来运行三种测试环境。
还可以通过java命令的方式。
java junit.textui.TestRunner com.test.UserAccountTest
这种方式也适合以上三种环境。
如何测试工程中所有的测试用例:
我们的项目会有很多测试用例,测试用例是同代码基本上同步,当项目基本完成,我们希望运行一下全部的测试用例,这个时候怎么办呢,不可能一个个运行。
实际上JUnit已经提供了这种测试的方法。而且用到了Composite的设计模式,主要是作者太强。
我们看看如何实现的。
public class TestAll extends TestCase{
public static Test suite(){
TestSuite test=new TestSuite();
test.addTestSuite(UserAccountTest.class);
test.addTestSuite(UserDaoTest.class);
。。。。。
return test;
}
}
这个TestAll也是需要继承TestCase.
定义suite的方法必须按照这种方式定义。
public static Test suite()
注:TestCase实现了Test接口,TestSuite实现了Test接口。
TestSuite是一个动态Test的合成类(Composite),可以运行一个Test集合。
Failure和Error
在JUnit中当运行结果可能出现问题,这个问题分为两种情况,一种是Error一种是Failure。
Error:执行断言之前就已经发生的异常,是由于程序本身的的错误,导致运行的异常,而非由于断言的错误。
Failure:执行断言异常,实际运行与预期运行结果不同导致,应该是由于测试程序运行中出现的错误。
可以理解为Error是本身程序错误,Failure是由于测试程序错误。
基本上JUnit3.8版本的知识点就这些了,缺少的可能是同ant的整合,这个有时间会补充上来。
接下来会将JUnit4的使用方法发出来。