本文作者通过实例介绍了单元测试自动化实现的原理和方法,更难得的是,作者结合自身工作经验提出了单元测试工程实施的要领和注意事项。
单元测试(Unit Testing)是针对于软件基本组成单元所进行的一种测试。按照《详细设计规格说明》中对软件单元的划分,单元测试人员应逐一检查软件单元的程序编码是否和设计要求完全一致。这里用到了“软件单元”这个词汇。一般地,“软件单元”是指在《详细设计规格说明》中所划分出的基本软件单元,即根据概要设计规格说明中的模块,细化出来的类、数据结构、过程或函数等。但在实际的单元测试过程中,有时为了进一步查找问题产生的根源,还会对软件单元继续细化,具体到某一个函数或方法体,或者函数、方法体内的某几段关键代码。
实现单元测试的自动化
目前,单元测试一般采用基于XUnit测试框架的自动化测试工具实现。如Java编程中使用的JUnit,.Net程序编程中使用的NUnit。也有一些其他的用于单元测试的工具,如Cantata或AdaTest,前者是针对于C/C++的测试工具,后者是针对于Ada语言的测试工具。单元测试工具的基本工作原理如图所示。
从图中可以看出,自动化单元测试工具的工作原理是借助于驱动模块与桩模块,运行被测软件单元以检查输入的测试用例是否按软件详细设计规格说明的规定执行相关操作。
这里必须先对桩模块(Stub Module)、驱动模块(Drive Module)等概念做一个简单的介绍。我们知道,软件单元在完成编码以后,代码本身并不是一个可以独立运行的程序。所以,必须为每个软件单元开发用于测试目的驱动模块和桩模块。在绝大多数应用中,驱动模块只是一个接收测试数据,并把数据传送给要测试的软件单元,然后打印或告知测试者相关测试结果的“主程序”;而桩模块的功能则是代替那些隶属于被测软件单元或与被测单元有接口关系的软件模块。这样,测试用例从驱动模块读入到被测软件单元中,被测软件单元针对给定的测试用例运行,当需要通过接口与其他模块进行通信时,就调用桩模块。被测软件单元执行完测试用例以后,将执行结果汇报给驱动模块,驱动模块再将执行结果打印出来或以其他方式(如E-mail)报告给单元测试者。
可以通过如下一个简单的Java程序来说明单元测试的原理。这个程序由三个代码文件组成。它们分别是CaseCheck.java、Account.java以及MoneyTran.java。其中CaseCheck.java充当驱动模块,Accout.java是被测软件单元,MoneyTran.java充当桩模块。以下列出它们各自的源代码:
/* Module name: CaseCheck.java this module servers as driven module; */ public class CaseCheck{ public static void main(String[] args){ Account TomAccount=new Account(8000); if(8000!=TomAccount.checkBalance()){ System.out.println("TomAccount Construction error!");} System.out.println("Total balance of TomAccount is "+TomAccount.checkBalance() +"\nWithdraw 1000 from TomAccount\n"+TomAccount.withdraw(1000)); System.out.println("Now,total balance of TomAccount is "+TomAccount.checkBalance() +"\nWithdraw 8000 from TomAccount\n"+TomAccount.withdraw(8000)); System.out.println("Desopit 2000 to Tom's Account."); TomAccount.deposit(2000); if(9000!=TomAccount.checkBalance()){ System.out.println("Account class deposit method error!");} System.out.println("Now Tom's Account has Rmb "+TomAccount.checkBalance()+" .It can change into USDollar "+TomAccount.toDollar()); } } /* Module name: Account.java this module servers as software unit for test; */ public class Account{ private int sum; public Account(int num){ sum=num; } public String withdraw(int num){ if(num>sum){return "Overdraft.Operation cancelled."+"\n";}else{ sum-=num; return "Withdraw Success."+"\n";}} public void deposit(int num){ sum+=num;} public int checkBalance(){return sum;} public int toDollar(){ double rate=MoneyTran.RmbtoDollar(); return (int) (rate*sum);} } /* Module name:MoneyTran.java this module servers as stub module; */ public class MoneyTran{ static double RmbtoDollar(){return 0.12081964;} } |