单元测试(三)
上一篇 / 下一篇 2008-04-02 14:55:22 / 个人分类:Eclipse插件开发
单元测试是整个测试流程中最基础的部分,它们要求程序员尽可能早地发现问题,并给予控制,另外如果集成测试出现问题,它可以帮助诊断。这样就为在软件开发流程中建立高效的事件反应机制打下了坚实基础。ITPUB个人空间s'\9O9cw+ad!X
s|!?3JdWP/x0为什么需要单元测试
e}U{V0ITPUB个人空间 KPE`D6M i
在开发软件的过程中,用户需要实际运行所编写的代码以确保程序的正确性。当软件变得越来越大,再去添加新的功能或做一些新的改动时,就很容易带来新的问题,甚至会使程序无法正常运行。然而要手动的运行代码,测试代码的可行性也是非常枯燥以及非常耗费时间的事情。
{,P9v@:XCB'J
N@0
IF2I#Bos7V)m0为了减少这种手动测试,可以通过创建单元测试来自动完成测试的工作。当修改代码或者添加新功能后,可以执行单元测试来保证代码运行无误。所有测试工作都是由单元测试自动完成的,开发人员所要做的就是停下来喝杯茶,看看程序的执行状态。
~+ok9F.P:qf*o0使用单元测试的另一个理由是实现测试驱动的开发,这在当前是比较流行的开发方式。测试驱动的开发尝试首先写出单元测试,然后完成实际的代码。通过单元测试来提供类的定义,当实际开始编写代码时,用户仅仅需要做的就是具体类的实现,只要单元测试运行通过,代码的实现也将告一段落了。写单元测试的同时,也在同时在做项目的设计,当项目结束后,单元测试还将是不错的文档,何乐而不为呢?ITPUB个人空间#sI[%T0~-i1l/{0f5K
`6n&I6W-KbxcJ6p L0自信编码ITPUB个人空间[E [%TZ2aPH6d
ITPUB个人空间~D2H4qs
人的记忆是短暂的,但代码的修改是无限的,怎样让无限的修改不会因为记忆的短暂而带来无穷的烦恼呢?这是非常矛盾的事情,单元测试能够一定程序上解决这个问题。
(ulQ~^0ITPUB个人空间7PH)^"xM(OY5J
也许所有的程序员都遇到过这样的问题,当要修改很久以前的代码或他人编写的代码时,总是会很犹豫,因为他们不清楚所做的修改会不会引起其它的问题,只能当遍历了所有的代码后才敢动手。这是非常正常的,但也因此浪费了很多的时间,通过单元测试能够一定程度上增强用户的自信心,因为单元测试的前提假设就是,如果通过了所有的测试,代码就是可行的。ITPUB个人空间2[!s]!oiRx6w
ITPUB个人空间8bvW+R6S0?D9Z
JUnit测试框架
&U DxFq0
,f:S
R8@&{1R"n0在Java语言中,可以通过JUnit框架进行单元测试,JUnit是由“Erich Gamma”和“Kent Beck”创建的,他们也是在“设计模式”和“极限编程”领域最伟大的作者之一。ITPUB个人空间pP:@.Y;BGA1]
GBe/f*g(hU0单元测试的实现是很简单的,可以认为它只是判断在某一个时刻,程序运行的值和预期的值是否一致,但在实际的应用的时候是很灵活的,在此介绍JUnit中的一些断言以及JUnit测试框架的使用,使读者能够快速的进入单元测试的领域,更快的进行开发。
FLLP{o?0
sOs-Ui`9t0JUnit提供了一些辅助函数,用于帮助开发人员确定某些被测试函数是否工作正常。通常而言,把所有这些函数统称为断言,断言是单元测试最基本的组成部分。
/L].j_ F~0ITPUB个人空间X&KwDhW}!f|
通常每种类型的断言都有两种形式,一种包含接收一个message参数,例如“static public void assertTrue(String message, boolean condition)”,message表示出错时的提示信息,另外一个则没有message参数。
Yg-R?iQ0ITPUB个人空间i/fnm&G9d!G-O2l
下面将分别介绍JUnit框架Assert类中的断言以及部分实现,每个函数的实现方法都为Assert类中定义的方法,读者也可以查看JUnit框架Assert类的实现代码。ITPUB个人空间*h#\M eZZ^?
Xcw#pqmK:K+|z0assertEquals断言ITPUB个人空间iEH%uv
ITPUB个人空间 `tV?(eI5C_&fc |.\
这是应用非常广泛的一个断言,它的作用是比较实际的值和用户预期的值是否一样,assertEquals在JUnit中有很多不同的实现,以参数expected和actual都为Object类型的为例,assertEquals定义为:
staticpublicvoidassertEquals(String message, Object expected, Object actual) {if(expected==null&&actual==null)return;if(expected!=null&&expected.equals(actual))return; failNotEquals(message, expected, actual); }
其中,expected为用户期望某一时刻对象的值,actual为某一时刻对象实际的值。如果这两值相等的话(通过对象的equals方法比较),说明预期是正确的,也就是说,代码运行是正确的。assertEquals还提供了其它的一些实现,例如整数比较,浮点数的比较等等。ITPUB个人空间{^_
L3o)H,c
#rJC9z#jS-d0assertTrue与assertFalse断言ITPUB个人空间0t?2I8Y"l+|.r*~1J
ITPUB个人空间*a+vyr{Ev0QI
assertTrue与assertFalse可以判断某个条件是真还是假,如果和预期的值相同则测试成功,否则将失败,assertTrue的定义如下:
staticpublicvoidassertTrue(String message, boolean condition) {if(!condition) fail(message); }
“condition”表示要测试的状态,如果“condition”的值为false,则测试将会失败。ITPUB个人空间 fCZ(o
f
ITPUB个人空间t1{#n:WQ\ U+Ct
assertNull与assertNotNull断言ITPUB个人空间4`[(n \3X.k3R!~,u'Z
ITPUB个人空间C+AsD1_.\
assertNull与assertNotNull可以验证所测试的对象是否为空或不为空,如果和预期的相同则测试成功,否则测试失败,assertNull定义为:
staticpublicvoidassertNull(String message, Objectobject) { assertTrue(message,object==null); }
其中,object就是要测试的对对象,如果object为空,该测试成功,否则失败,是不是很简单。ITPUB个人空间 k
DO Y)[+^m3\
Om]
ITPUB个人空间X(}9AsO S
assertSame与assertNotSame断言ITPUB个人空间XOLG7[
/K.V({pd.rr3n0assertSame和assertEquals不同,assertSame测试预期的值和实际的值是否为同一个参数(即判断是否为相同的引用)。assertNotSame则测试预期的值和实际的值是不为同一个参数。assertSame的定义为:
staticpublicvoidassertSame(String message, Object expected, Object actual) {if(expected==actual)return; failNotSame(message, expected, actual); }
而assertEquals则判断两个值是否相等,通过对象的equals方法比较,可以相同引用的对象,也可以不同。
$Z2VP&I+W0k gq|0
+p!qs%Gi0fail断言
NwE6N2Xg0
y_,Fh;Eex j0“fail”断言能使测试立即失败,这种断言通常用于标记某个不应该被到达的分支。例如assertTrue断言中,condition为false时就是正常情况下不应该出现的,所以测试将立即失败,fail的定义为:
staticpublicvoidfail(String message) {thrownewAssertionFailedError(message); }
当一个失败或者错误出现的时候,当前测试方法的执行流程将会被中止,但是位于同一个测试类中的其他测试将会继续运行。
mWT.dBV(G9w0
pH X4xK2B0JUnit是为Java程序开发者实现单元测试的一个框架,它能使得Java单元测试更规范有效,并且更有利于测试的集成。
_0EQ;ZK7eb|%Y*eB0
j C:Z8a4[L7A0TestCase测试类
b `fa~o1t-BeC k7k0ITPUB个人空间%bNer-A
前面已经介绍了JUnit中部分断言实现,但是不能够把断言直接写到源代码中,最终运行发布的代码和测试应该是分离的两个部分,源程序是不应该知道有任何的单元测试的存在的。
/G f0^ OOd%a0
Qz]2k?6Ht x ? i0JUnit框架中通过TestCase 实现单元测试,TestCase继承了Assert类,也就是说在TestCase类以及子类中可以直接使用JUnit框架所提供的断言。另外,TestCase实现了Test接口,Test接口的run方法将会运行一个测试,并返回结果。TestCase类的继承关系及方法的实现如图1所示:
ITPUB个人空间W2f-O
D+|
.SV8L(XK1u*K)k&N0图1 TestCase的类继承关系
Q dOqz/|3q4LN0如果需要使用JUnit做单元测试,可以继承TestCase,每一个TestCase的子类可以作为独立的测试用例,从而实现测试的目的。
l7zNp} c;C5p-~0
QPE
OF0TestSuite测试组
_5CQ N4{1Jn0
%gx*g!\4etTJ0一个TestCase用例会包含多个test开头的测试函数,而一个应用中,会包括若干个TestCase的用例,通常用户会运行应用中的所有的测试,从而确保应用是可以运行的。有些测试是非常耗费时间,如果每次进行一个小的改动就运行所有的测试,这是很多开发人员不可接受的,这时就需要组合一些TestCase用例以及TestCase中的方法,把测试分成很多的组,每次只针对特定的组进行测试。