出处
作者:许式伟
写于:2005年3月
背景:
WPS Office 2005 (开发代号: V6)
原文:
why-unit-test.pdf
(pdf格式)
目录
- 自动化单元测试的重要特征
- 单元测试的项目意义
- 单元测试对设计的意义
- cppunit for v6
- 将测试案例写在dll中
常规测试的缺陷
- 一般是基于手工的,不具备可回归性。因此测试的效率不高。
- 由于缺乏效率,往往导致测试仅仅针对典型数据,覆盖面往往也很低。
自动化单元测试的重要特征
- 自动化、可回归性
- Quiet
- 案例的执行安全受控
自动化、可回归性
-
测试的结果是程序直接检测的,而不是
“通过人眼对屏幕上的输出结果的观测”
。
- 测试的输入数据和预测结果直接在案例中, 除非结果非预期,否则不需要任何输出 。
- 因此,并不推荐屏幕输出,或者写可视化的测试单元。
- 案例的执行不需要人工干预,可随时回放。
- 因此,我们可以通过大量典型测试数据进行回归,以确保系统的可靠性。
-
对比:常规测试典型过程
- 在屏幕上提示输入数据,然后打印结果,通过查看屏幕显示的结果,确认我们的系统是否存在问题。
Quiet
- 绝对避免在案例执行过程中弹出对话框或者进行其他UI交互。
- (调试模式下例外)
案例的执行安全受控
- 某个测试案例执行的失败(甚至出现了异常),不会导致测试环境的崩溃,也不影响另一个案例的执行结果。
常规测试样例
void main ()
{
int n ;
for ( ;; )
{
std :: cout << " input n(0 to exit): " ;
std :: cin >> n ;
if ( n == 0 )
break ;
std :: cout << " n! = " << fact ( n ) << std :: endl ;
}
}
cppunit测试案例
class TestFact : public TestCase
{
public :
CPPUNIT_TEST_SUITE ( TestFact ) ;
CPPUNIT_TEST ( test ) ;
CPPUNIT_TEST_SUITE_END () ;
void test ()
{
CPPUNIT_ASSERT ( fact ( 3 ) == 6 ) ;
CPPUNIT_ASSERT ( fact ( 4 ) == 24 ) ;
CPPUNIT_ASSERT ( fact ( 5 ) == 120 ) ;
}
} ;
CPPUNIT_TEST_SUITE_REGISTRATION ( TestFact ) ;
单元测试的项目意义
- 模块更加可控。
- 及早发现问题,提高模块质量,降低系统测试成本。
- 加速开发周期。
单元测试与项目控制
-
单元测试不应该是程序员的个人行为,而与项目进度控制、质量控制息息相关。
- 通过监测单元测试的情况,来量化模块开发的进度与质量。
-
要改善项目的控制,必须“做好单元测试”。
- 不止是要让单元测试成为每个程序员的例行公事,而且要让它变得规范、变得受控。
单元测试的误区
- 一个很常见误解是:嗯,单元测试,好事啊,只是最近时间很紧,下回做吧。
-
实际上
单元测试大家都会去做
,我们提倡单元测试,更大程度上是要达到:
- 规范单元测试的编写(手段);
- 最大程度保留下来我们的测试案例;
加速开发周期
-
一个系统自动化程度越高,则运营成本越低。
- 单元(模块)是最容易,也是最应该采用自动化测试的。而集成测试、系统测试虽然也有自动化的方法和支持工具,但需要更高额的代价。
-
单元测试
将问题的发现周期缩短,降低bug修复成本。
- 如果问题在集成测试、系统测试期被发现,那么同样一个问题需要花几倍甚至几十倍的时间进行定位、修复。
- 单元测试降低了集成测试、系统测试的压力和投入成本。
单元测试的主要检查点
- 案例的覆盖度
- Bug量与Bug反复情况
- 案例文档的规范
单元测试对设计的意义
- 设计应该以可测试性为第一目标。
- 及时发现模块构架调整引发的潜在Bug。
模块的可测试性
-
可测试性 = 低耦合
- 环境模拟(依赖的模块、数据输入)
- 模块设计应该首先保证可测试性。
- 坚持进行单元测试可提高设计能力。
- 一个模块可以很方便地进行测试,那么就可以说它是一个设计优良的模块。
发现模块构架调整的潜在Bug
- 通常 模块在架构调整期(代码重构)最容易引入Bug。
- 只有在模块开发中就不断积累经典数据、以案例的形式固化已知Bug,才可能在架构调整等最容易引发问题的情形下获得最佳效果。
cppunit for v6
- 引入调试模式
- 通用的调试开关
- 有选择的运行测试案例
- 独特的dll测试模式
调试模式
-
同一套测试代码,两种工作模式。
- cppunit提供了安全可控的自动化测试平台。
- 但是是代码总会有bug。为了让测试案例可调试,v6在开源的cppunit基础上,结合kfc的调试策略,引入调试模式。
通用的调试开关
-
/ndebug
- 进入自动化测试模式。默认为调试模式。
-
/output:<xmlfile>
- 输出结果到xml文件。注意:/output:后不能有空格。
- 默认输出到控制台。
-
/run:<testclass>[.<testmethod>]
- 执行<testclass>类所有案例,或者执行<testclass>的<testmethod>案例。
有选择的运行测试案例
- 你可以只执行某个TestCase类,或者只是执行某个TestCase中的某个method。
- 演示rununit程序的参数含义:
<testclass> <testmethod>
- 演示通用开关:
/run:<testclass>[.<testmethod>]
将测试案例写在dll中
- 测试案例同步更新
- 测试dll内部组件
- 关于rununit工具
基于dll的单元测试
-
对比
- 为dll写测试程序(exe方式)
-
优点
-
方便测试案例的同步更新
- 并且强迫你同步更新测试案例
- 可测试dll的内部组件
- 去除了建立新工程的过程(有点麻烦)。
-
方便测试案例的同步更新
关于rununit工具
-
用rununit调试你的dll
- rununit程序是一个通用的testcaseRunner,可用于执行(或调试)一个dll中的部分或全部案例。
- 命令行
rununit testcase[.dll] [testclass] [testmethod] [/ndebug] [/pause] [/output:<xmlfile>]
Visual Studio中调试dll
今天,你“单元测试”了吗?
- 如果你没有,那么我告诉你,你已经落伍啦!
附录
- rununit.exe: 参考ksdn中关于该程序的详细说明。
- CreateCppUnitTestCase宏: 参考KSRule.dsm(macros for Visual Studio)。