全面介绍单元测试
上一篇 / 下一篇 2007-04-26 17:19:39 / 个人分类:study
单元测试概述
5T{qvU3E `(a2lYj0 工厂在组装一台电视机之前,会对每个元件都进行测试,这,就是单元测试。51Testing软件测试网(EP-sfY2u'r#\
其实我们每天都在做单元测试。你写了一个函数,除了极简单的外,总是要执行一下,看看功能是否正常,有时还要想办法输出些数据,如弹出信息窗口什么的,这,也是单元测试,老纳把这种单元测试称为临时单元测试。只进行了临时单元测试的软件,针对代码的测试很不完整,代码覆盖率要超过70%都很困难,未覆盖的代码可能遗留大量的细小的错误,这些错误还会互相影响,当BUG暴露出来的时候难于调试,大幅度提高后期测试和维护成本,也降低了开发商的竞争力。可以说,进行充分的单元测试,是提高软件质量,降低开发成本的必由之路。
e#\!A ?$It3u5aM,^0 对于程序员来说,如果养成了对自己写的代码进行单元测试的习惯,不但可以写出高质量的代码,而且还能提高编程水平。
VY g(CKI)^0 要进行充分的单元测试,应专门编写测试代码,并与产品代码隔离。老纳认为,比较简单的办法是为产品工程建立对应的测试工程,为每个类建立对应的测试类,为每个函数(很简单的除外)建立测试函数。首先就几个概念谈谈老纳的看法。51Testing软件测试网Z4Y@8La$Y c/q
一般认为,在结构化程序时代,单元测试所说的单元是指函数,在当今的面向对象时代,单元测试所说的单元是指类。以老纳的实践来看,以类作为测试单位,复杂度高,可操作性较差,因此仍然主张以函数作为单元测试的测试单位,但可以用一个测试类来组织某个类的所有测试函数。单元测试不应过分强调面向对象,因为局部代码依然是结构化的。单元测试的工作量较大,简单实用高效才是硬道理。51Testing软件测试网+wCAH4W9At h&c
有一种看法是,只测试类的接口(公有函数),不测试其他函数,从面向对象角度来看,确实有其道理,但是,测试的目的是找错并最终排错,因此,只要是包含错误的可能性较大的函数都要测试,跟函数是否私有没有关系。对于C++来说,可以用一种简单的方法区隔需测试的函数:简单的函数如数据读写函数的实现在头文件中编写(inline函数),所有在源文件编写实现的函数都要进行测试(构造函数和析构函数除外)。
\r{5dH4P:{t+R`0 什么时候测试?单元测试越早越好,早到什么程度?XP开发理论讲究TDD,即测试驱动开发,先编写测试代码,再进行开发。在实际的工作中,可以不必过分强调先什么后什么,重要的是高效和感觉舒适。从老纳的经验来看,先编写产品函数的框架,然后编写测试函数,针对产品函数的功能编写测试用例,然后编写产品函数的代码,每写一个功能点都运行测试,随时补充测试用例。所谓先编写产品函数的框架,是指先编写函数空的实现,有返回值的随便返回一个值,编译通过后再编写测试代码,这时,函数名、参数表、返回类型都应该确定下来了,所编写的测试代码以后需修改的可能性比较小。
V$vBC8^#@w#j(B8k0 由谁测试?单元测试与其他测试不同,单元测试可看作是编码工作的一部分,应该由程序员完成,也就是说,经过了单元测试的代码才是已完成的代码,提交产品代码时也要同时提交测试代码。测试部门可以作一定程度的审核。51Testing软件测试网8j"Y7|8Y,^N+R&St+A
关于桩代码,老纳认为,单元测试应避免编写桩代码。桩代码就是用来代替某些代码的代码,例如,产品函数或测试函数调用了一个未编写的函数,可以编写桩函数来代替该被调用的函数,桩代码也用于实现测试隔离。采用由底向上的方式进行开发,底层的代码先开发并先测试,可以避免编写桩代码,这样做的好处有:减少了工作量;测试上层函数时,也是对下层函数的间接测试;当下层函数修改时,通过回归测试可以确认修改是否导致上层函数产生错误。
AC*vYbp(Z7b dx5p0
UB/eh8?E%U;\0二 测试代码编写51Testing软件测试网,[e5{l,OZ,\
多数讲述单元测试的文章都是以Java为例,本文以C++为例,后半部分所介绍的单元测试工具也只介绍C++单元测试工具。下面的示例代码的开发环境是VC6.0。51Testing软件测试网2sLi4c4^v gB
51Testing软件测试网.V2aiw0ZSh8r k
产品类:
[3e!_;w'V#cY0class CMyClass51Testing软件测试网2D |De$G8_[C
{
2_vWeZyn6rU u5p0public:
m(xU-ZB9|I{0 int Add(int i, int j);51Testing软件测试网;t1lnY R @u4Z;I
CMyClass();51Testing软件测试网KboOxk[A6bb
virtual ~CMyClass();
ju8TH P:N{|(z0
g^]C"Wh ic5~!y-l0private:51Testing软件测试网i^,{0^{vg^
int mAge; //年龄51Testing软件测试网/?-u2m;X| ng-\ \S
CString mPhase; //年龄阶段,如"少年","青年"51Testing软件测试网)j0m9ufSK~!j#@8sl
};
%r)z^3D(_0?9n0
5dE-qF ^ L@O0建立对应的测试类CMyClassTester,为了节约编幅,只列出源文件的代码:51Testing软件测试网8J$Z9s%fM/@m
void CMyClassTester::CaseBegin()51Testing软件测试网nz.A(n'S\
{
4P\O2S0s0 //pObj是CMyClassTester类的成员变量,是被测试类的对象的指针,51Testing软件测试网7SWeUv
//为求简单,所有的测试类都可以用pObj命名被测试对象的指针。
+B)]+IKYnw2m[0 pObj = new CMyClass();51Testing软件测试网gS~ htn0tp ~
}
U9K;|2U1~a'T}O9{051Testing软件测试网rvU Z#m3B)c&T
void CMyClassTester::CaseEnd()
.o6\!bf2cc0{
C[jb Bl M0 delete pObj;
*b(_J7H^+oX0}51Testing软件测试网,k S8D!oeH
测试类的函数CaseBegin()和CaseEnd()建立和销毁被测试对象,每个测试用例的开头都要调用CaseBegin(),结尾都要调用CaseEnd()。
[v(D(Jm+bpK"@ B'u0
5T{qvU3E `(a2lYj0 工厂在组装一台电视机之前,会对每个元件都进行测试,这,就是单元测试。51Testing软件测试网(EP-sfY2u'r#\
其实我们每天都在做单元测试。你写了一个函数,除了极简单的外,总是要执行一下,看看功能是否正常,有时还要想办法输出些数据,如弹出信息窗口什么的,这,也是单元测试,老纳把这种单元测试称为临时单元测试。只进行了临时单元测试的软件,针对代码的测试很不完整,代码覆盖率要超过70%都很困难,未覆盖的代码可能遗留大量的细小的错误,这些错误还会互相影响,当BUG暴露出来的时候难于调试,大幅度提高后期测试和维护成本,也降低了开发商的竞争力。可以说,进行充分的单元测试,是提高软件质量,降低开发成本的必由之路。
e#\!A ?$It3u5aM,^0 对于程序员来说,如果养成了对自己写的代码进行单元测试的习惯,不但可以写出高质量的代码,而且还能提高编程水平。
VY g(CKI)^0 要进行充分的单元测试,应专门编写测试代码,并与产品代码隔离。老纳认为,比较简单的办法是为产品工程建立对应的测试工程,为每个类建立对应的测试类,为每个函数(很简单的除外)建立测试函数。首先就几个概念谈谈老纳的看法。51Testing软件测试网Z4Y@8La$Y c/q
一般认为,在结构化程序时代,单元测试所说的单元是指函数,在当今的面向对象时代,单元测试所说的单元是指类。以老纳的实践来看,以类作为测试单位,复杂度高,可操作性较差,因此仍然主张以函数作为单元测试的测试单位,但可以用一个测试类来组织某个类的所有测试函数。单元测试不应过分强调面向对象,因为局部代码依然是结构化的。单元测试的工作量较大,简单实用高效才是硬道理。51Testing软件测试网+wCAH4W9At h&c
有一种看法是,只测试类的接口(公有函数),不测试其他函数,从面向对象角度来看,确实有其道理,但是,测试的目的是找错并最终排错,因此,只要是包含错误的可能性较大的函数都要测试,跟函数是否私有没有关系。对于C++来说,可以用一种简单的方法区隔需测试的函数:简单的函数如数据读写函数的实现在头文件中编写(inline函数),所有在源文件编写实现的函数都要进行测试(构造函数和析构函数除外)。
\r{5dH4P:{t+R`0 什么时候测试?单元测试越早越好,早到什么程度?XP开发理论讲究TDD,即测试驱动开发,先编写测试代码,再进行开发。在实际的工作中,可以不必过分强调先什么后什么,重要的是高效和感觉舒适。从老纳的经验来看,先编写产品函数的框架,然后编写测试函数,针对产品函数的功能编写测试用例,然后编写产品函数的代码,每写一个功能点都运行测试,随时补充测试用例。所谓先编写产品函数的框架,是指先编写函数空的实现,有返回值的随便返回一个值,编译通过后再编写测试代码,这时,函数名、参数表、返回类型都应该确定下来了,所编写的测试代码以后需修改的可能性比较小。
V$vBC8^#@w#j(B8k0 由谁测试?单元测试与其他测试不同,单元测试可看作是编码工作的一部分,应该由程序员完成,也就是说,经过了单元测试的代码才是已完成的代码,提交产品代码时也要同时提交测试代码。测试部门可以作一定程度的审核。51Testing软件测试网8j"Y7|8Y,^N+R&St+A
关于桩代码,老纳认为,单元测试应避免编写桩代码。桩代码就是用来代替某些代码的代码,例如,产品函数或测试函数调用了一个未编写的函数,可以编写桩函数来代替该被调用的函数,桩代码也用于实现测试隔离。采用由底向上的方式进行开发,底层的代码先开发并先测试,可以避免编写桩代码,这样做的好处有:减少了工作量;测试上层函数时,也是对下层函数的间接测试;当下层函数修改时,通过回归测试可以确认修改是否导致上层函数产生错误。
AC*vYbp(Z7b dx5p0
UB/eh8?E%U;\0二 测试代码编写51Testing软件测试网,[e5{l,OZ,\
多数讲述单元测试的文章都是以Java为例,本文以C++为例,后半部分所介绍的单元测试工具也只介绍C++单元测试工具。下面的示例代码的开发环境是VC6.0。51Testing软件测试网2sLi4c4^v gB
51Testing软件测试网.V2aiw0ZSh8r k
产品类:
[3e!_;w'V#cY0class CMyClass51Testing软件测试网2D |De$G8_[C
{
2_vWeZyn6rU u5p0public:
m(xU-ZB9|I{0 int Add(int i, int j);51Testing软件测试网;t1lnY R @u4Z;I
CMyClass();51Testing软件测试网KboOxk[A6bb
virtual ~CMyClass();
ju8TH P:N{|(z0
g^]C"Wh ic5~!y-l0private:51Testing软件测试网i^,{0^{vg^
int mAge; //年龄51Testing软件测试网/?-u2m;X| ng-\ \S
CString mPhase; //年龄阶段,如"少年","青年"51Testing软件测试网)j0m9ufSK~!j#@8sl
};
%r)z^3D(_0?9n0
5dE-qF ^ L@O0建立对应的测试类CMyClassTester,为了节约编幅,只列出源文件的代码:51Testing软件测试网8J$Z9s%fM/@m
void CMyClassTester::CaseBegin()51Testing软件测试网nz.A(n'S\
{
4P\O2S0s0 //pObj是CMyClassTester类的成员变量,是被测试类的对象的指针,51Testing软件测试网7SWeUv
//为求简单,所有的测试类都可以用pObj命名被测试对象的指针。
+B)]+IKYnw2m[0 pObj = new CMyClass();51Testing软件测试网gS~ htn0tp ~
}
U9K;|2U1~a'T}O9{051Testing软件测试网rvU Z#m3B)c&T
void CMyClassTester::CaseEnd()
.o6\!bf2cc0{
C[jb Bl M0 delete pObj;
*b(_J7H^+oX0}51Testing软件测试网,k S8D!oeH
测试类的函数CaseBegin()和CaseEnd()建立和销毁被测试对象,每个测试用例的开头都要调用CaseBegin(),结尾都要调用CaseEnd()。
[v(D(Jm+bpK"@ B'u0