软件测试系列之单元测试(2 CppUnit Framework )

来源:互联网 发布:linux doc 命令 编辑:程序博客网 时间:2024/04/28 05:20

软件测试系列之单元测试(2 CppUnit Framework )

 

整理资料时发现以前给兄弟们灌输的单元测试的一些基本知识,放在这里供大家参考。里面参考了网上很多朋友的资料,这里没一一列出,一并谢过。ppt转的,比较乱,ppt已经上传到个人资源的,需要的可以去下载看看。不知道怎么搞的,ppt转成word格式是正确的,word里粘贴到这里,在编辑方式下也是对的,可是浏览文章时就乱七八糟了。只有在记事本里中转了一下,丧失了很多排版上的信息。

 


现象
? 测试代码没有很好地维护而废弃,再次需要测试时还需要重写;
? 投入太多的精力,找 bug,而新的代码仍然会出现类似 bug;
? 写完代码,心里没底,是否有大量 bug 等待自己;
? 新修改的代码不知道是否影响其他部分代码;
? 由于牵扯太多,导致不敢进行修改代码;
...


一、背景
? CppUnit 是个基于 LGPL 的开源项目,最初版本移植自 JUnit,是一个非常优秀的开源测试框架。
? CppUnit 和 JUnit 一样主要思想来源于极限编程(XProgramming)。
? 主要功能就是对单元测试进行管理,并可进行自动化测试。
? “现象” ? 就应该学习使用这种技术
二、感性认识
? CppUnit安装与使用方法
? VC6
? VS2005
? QNX
? CppUnit快速入门
CppUnit VC6.0安装
? 重要参考:INSTALL-WIN32.txt
? 解压cppunit-1.12.0.tar.gz
? Building:---------
? * Open the src/CppUnitLibraries.dsw workspace in VC++.
? * In the 'Build' menu, select 'Batch Build...'
? * In the batch build dialog, select all projects and press the build button.
? * The resulting libraries can be found in the lib/ directory.
? 注意:直接编译会有错误
CppUnit VC6.0安装
? 注册插件
? At the current time, the only supported WIN32 platform is
? Microsoft Visual C++. You must have VC++ 6.0 at least.
? Quick Steps to compile & run a sample using the GUI TestRunner:
? - Open examples/examples.dsw in VC++ (contains all the samples).
?   VC7 will ask you if you want to convert, anwser 'yes to all'.
? - Make HostApp the Active project
? - Compile

? - For Visual Studio 6 only:
?    - in VC++, Tools/Customize.../Add-ins and macro files/Browse...
?    - select the file lib/TestRunnerDSPlugIn.dll and press ok to register
?      the add-ins (double-click on failure = open file in VC++).
? - Run the project
CppUnit VC 设置
? “Projects/Settings.../C++/C++ Language”页选中“Enable RTTI ”
   RTTI---Run-Time Type Information
? 在“Projects/Settings.../C++/Code Generation”页选择“Use run-time library”中的内容:
Release版, 选择"Mulithreaded DLL".
Debug版, 选择 "Debug Multihreaded DLL".
? Project Settings/Post-Build step增加: $(TargetPath)
CppUnit VS2005安装
? 1、 Open examples/examples.dsw in VC++ (contains all the samples).
?   VC7 will ask you if you want to convert, anwser 'yes to all'.
? 2、解决方案上右键,批量编译,选择全部,编译
? 3、错误:
? #import "libid:80cc9f66-e7d8-4ddd-85b6-d9e6cd0e93e2" version
? ("7.0") lcid("0") raw_interfaces_only named_guids
? 改为:
? #import "libid:80cc9f66-e7d8-4ddd-85b6-d9e6cd0e93e2" version
? ("8.0") lcid("0") raw_interfaces_only named_guids
? 4、d:/cppunit-1.12.0/cppunit-1.12.0/src/msvc6/dsplugin/stdafx.h
? (12) : fatal error C1189: #error :  This add-in is for VC++ 6.0
? only.
? 不用处理
CppUnit VS2005 向导
? 0. 下载 CPPUnitProjectWizard
? 1. 复制文件
?      CPPUnitProjectWizard.vsdir  - 为向导命名
?      CPPUnitProjectWizard.vsz    - 让VS8知道从哪里找到向导
?     到您的Visual Studio 8安装目录下的 VSProjects 文件夹中   
? 2. 把整个CPPUnitProjectWizard解决方案文件夹复制到您的Visual Studio 8安装目录下的VCWizards文件夹中。
?    比如,我放在c:/Program Files/Microsoft Visual Studio 8/VC/VCWizards/CPPUnitProjectWizard/CPPUnitProjectWizard
?    或者,也可以放在你想放置的其它地方,然后编译CPPUnitProjectWizard.vsz,定义参数 ABSOLUTE_PATH
?    Param="ABSOLUTE_PATH = c:/Program Files/Microsoft Visual Studio 8/VC/VCWizards/CPPUnitProjectWizard/CPPUnitProjectWizard"  
? 3. 该项目需要定义环境变量 CPPUNITDIR
?    比如,我的环境变量  %CPPUNITDIR% = D:/cppunit-1.12.0
?    最后,修改 环境变量 %PATH%,在PATH路径中,增加 %CPPUNITDIR%/lib,以便程序加载时能找到 cppunit_dll.dll
? 4. 在开发环境中,设置好Include/Lib路径
?     %CPPUNITDIR%/Include
?     %CPPUNITDIR%/LIB

CppUnit QNX
? 参考工程模板
? 修改buildcfg.bat
? Qnx的安装环境设置
? QNXDISK:qnx安装在的分区
? SUNYPRJPATH:工程根路径
? 调用build.bat编译
? 举例说明
CppUnit快速入门
? CppUnit本身的测试
? Example0--快速入门
三、基本理论
? 什么是 UnitTest Framework
? 什么是 CppUnit
? CppUnit 架构
? 产品代码与测试代码关系
? Unit Test与 TDD(测试驱动开发)
? CppUnit的原理

什么是UnitTest Framework
? 单元测试框架是编写和运行单元测试的软件工具,用来构建测试、运行测试、报告测试结果
 
? unit test famework 的历史以及 CppUnit
   JUnit -> xUnit(含CppUnit )

? xUnit 家族
  Junit - The reference implementation of xUnit
  CppUnit - The C++ port of JUnit
  NUnit - The xUnit for .NET
  PyUnit - The Python version of xUnit
  MinUnit - minimal but functional C Languages unit test framework
   XmlUnit – for XML contents
什么是 CppUnit
? CppUnit是个基于 LGPL 的开源项目,最初版本移植自 JUnit ,是一个非常优秀的开源测试框架。CppUnit和 JUnit 一样主要思想来源于极限编程(XProgramming)。主要功能就是对单元测试进行管理,并可进行自动化测试。
CppUnit 架构--- namespace 
CppUnit 架构--- key classes  
CppUnit 架构--- classes to collect test results  
CppUnit 架构--- outputter classes for printing test results  
产品代码与测试代码关系
? 产品代码和测试代码的目录结构示意图
产品代码与测试代码关系
? 产品代码与测试框架关系示意图
Unit Test与 TDD(测试驱动开发)
? 测试驱动开发精髓
? TDD循环
 “Test twice, code once” - 测试两次,编码一次。
? 测试驱动开发的原则
测试驱动开发精髓
? 维护详尽的程序员编写的测试程序组
? 除非有相关的测试,否则代码不应被加入产品(“极限编程”,因为测试是重要的,所以对几乎所有代码都要有测试)
? 测试先行
? 测试决定你需要写的代码
TDD循环
“Test twice, code once” 
? 编写新代码的测试,查看是否失败
? 编写新代码,以最简方式实现
? 再次测试是否成功,重构代码
测试驱动开发的原则
? 先写测试代码,然后编写符合测试的代码。至少做到完成部分代码后,完成对应的测试代码;
? 测试代码不需要覆盖所有的细节,但应该对所有主要的功能和可能出错的地方有相应的测试用例;
? 发现 bug,首先编写对应的测试用例,然后进行调试;
? 不断总结出现 bug 的原因,对其他代码编写相应测试用例;
? 每次编写完成代码,运行所有以前的测试用例,验证对以前代码影响,把这种影响尽早消除;
? 不断维护测试代码,保证代码变动后通过所有测试;
? 在编码前:他可以强迫你对需求进行详细的分析。
? 在编码时:他可以使你对over coding保持警觉。
? 在重构时:可以确保新的设计能够兼容旧版本的功能。
? 在团队开发时:可以确保自己的单元是无误的。
CppUnit的原理
? Test
? TestFixture
? TestCase
? TestSuite
? ASSERT
CppUnit的原理--- Test
? //Test.h
? 测试类的抽象基类
? 规定了所有测试类都应该具有的行为
? 对应于Composite Pattern中的Component

CppUnit的原理--- TestFixture
? //TestFixture.h
? 一个或一组测试用例的测试对象被称为 Fixture
? Fixture就是被测试的目标
? 为一组相关的测试提供运行所需的公用环境
? 抽象类,用于包装测试类使之具有setUp方法和tearDown方法。
CppUnit的原理--- TestCase
? //TestCase.h,TestCase.cpp
? 多重继承:
? TestCase : public TestLeaf, public TestFixture
? TestLeaf: public Test
? TestCase :对这个 Fixture 的某个功能、某个可能出错的流程编写测试代码,这样对某个方面完整的测试被称为TestCase(测试用例)
CppUnit的原理--- TestCase步骤 
? 对 fixture 进行初始化,及其他初始化操作,比如:生成一组被测试的对象,初始化值;( setUp ())
? 按照要测试的某个功能或者某个流程对 fixture 进行操作;
? 验证结果是否正确;
? 对 fixture 的及其他的资源释放等清理工作 ( tearDown())
 运行时 CppUnit 会自动为每个测试用例函数运行 setUp,之后运行 tearDown,这样测试用例之间就没有交叉影响
CppUnit的原理--- TestCase注意点 
? 可以自动执行,不用人手操作。
? 自动返回测试结果。
? 绝对的独立,不能与其他TestCase有任何联系。就算测试同一个函数的不同功能也需要分开。每个TestCase可以说是一个孤岛。
? 例如
CppUnit的原理--- TestCase例子
CppUnit的原理--- ASSERT
? CPPUNIT_ASSERT(condition):判断condition的值是否为真,如果为假则生成错误信息。
? CPPUNIT_ASSERT_MESSAGE(message, condition):与CPPUNIT_ASSERT类似,但结果为假时报告messsage信息。
? CPPUNIT_FAIL(message):直接报告messsage错误信息。
? CPPUNIT_ASSERT_EQUAL(expected, actual):判断expected和actual的值是否相等,如果不等输出错误信息。
? CPPUNIT_ASSERT_EQUAL_MESSAGE(message, expected, actual):与CPPUNIT_ASSERT_EQUAL类似,但断言失败时输出message信息。
? CPPUNIT_ASSERT_DOUBLES_EQUAL(expected, actual, delta):判断expected与actual的偏差是否小于delta,用于浮点数比较。
? CPPUNIT_ASSERT_THROW(expression, ExceptionType):判断执行表达式expression后是否抛出ExceptionType异常。
? CPPUNIT_ASSERT_NO_THROW(expression):断言执行表达式expression后无异常抛出。
四、  核心内容
? 测试对象(Test,TestFixture,...)
用于开发测试用例,以及对测试用例进行组织管理 
? 测试结果(TestResult)
处理测试用例执行结果, Observer Pattern
? 测试结果监听者(TestListener)
  TestListener作为TestResult的观察者,担任实际的结果处理角色
? 结果输出(Outputter)
 将结果进行输出,可以制定不同的输出格式
? 对象工厂(TestFactory)
用于创建测试对象,对测试用例进行自动化管理
? 测试执行体(TestRunner)
用于运行一个测试
核心内容 ---Test
? 所有测试对象的基类
? CppUnit采用树形结构来组织管理测试对象,类似于目录树
  组合设计模式(Composite Pattern),Test的两个直接子类TestLeaf和TestComposite分别表示“测试树”中的叶节点和非叶节点,其中TestComposite主要起组织管理的作用,就像目录树中的文件夹,而TestLeaf才是最终具有执行能力的测试对象,就像目录树中的文件。
? Test最重要的一个公共接口为:
 virtual void run(TestResult *result) = 0;
 其作用为执行测试对象,将结果提交给result。
? 在实际应用中,一般不会直接使用Test、TestComposite以及TestLeaf,除非要重新定制某些机制。
核心内容 ---TestFixture
? 用于维护一组测试用例的上下文环境
? 在实际应用中,经常会开发一组测试用例来对某个类的接口加以测试,而这些测试用例很可能具有相同的初始化和清理代码。为此,CppUnit引入TestFixture来实现这一机制。
? TestFixture具有以下两个接口,分别用于处理测试环境的初始化与清理工作:
? virtual void setUp();
virtual void tearDown(); 
核心内容 ---TestCase
? 测试用例,从名字上就可以看出来,它便是单元测试的执行对象。
? TestCase从Test和TestFixture多继承而来,通过把Test::run制定成模板函数(Template Method)而将两个父类的操作融合在一起
? 这里要提到的是函数runTest,它是TestCase定义的一个接口,原型如下:
 virtual void runTest();
? 用户需从TestCase派生出子类并实现runTest以开发自己所需的测试用例。
核心内容 ---TestSuit
? 测试包,按照树形结构管理测试用例
? TestSuit是TestComposite的一个实现,它采用vector来管理子测试对象(Test),从而形成递归的树形结构。
核心内容 --- TestCaller
? TestCase适配器(Adapter),它将成员函数转换成测试用例
? 虽然可以从TestCase派生自己的测试类,但从TestCase类的定义可以看出,它只能支持一个测试用例,这对于测试代码的组织和维护很不方便,尤其是那些有共同上下文环境的一组测试。为此,CppUnit提供了TestCaller以解决这个问题
? TestCaller是一个模板类,它以实现了TestFixture接口的类为模板参数,将目标类中某个符合runTest原型的测试方法适配成TestCase的子类。
? 在实际应用中,大多采用TestFixture和TestCaller相组合的方式,详见后面的例子

核心内容 ---TestResult和TestListener
? 处理测试信息和结果
? TestResult和TestListener采用了观察者模式,TestResult维护一个注册表,用于管理向其登记过的TestListener,当TestResult收到测试对象(Test)的测试信息时,再一一分发给它所管辖的TestListener。这一设计有助于实现对同一测试的多种处理方式。
核心内容 ---TestFactory 
? 测试工厂
? 辅助类,通过借助一系列宏定义让测试用例的组织管理变得自动化。参见后面的例子
核心内容 --- TestRunner
? 用于执行测试用例
? TestRunner将待执行的测试对象管理起来,然后供用户调用。其接口为:
 virtual void addTest( Test *test ); virtual void run( TestResult &controller, const std::string &testPath = "" );
? 这也是一个辅助类,需注意的是,通过addTest添加到TestRunner中的测试对象必须是通过new动态创建的,用户不能删除这个对象,因为TestRunner将自行管理测试对象的生命期
核心内容 ---例1 
核心内容 ---例1 
核心内容 ---例1 
核心内容 ---例2 
核心内容 ---例2 
核心内容 ---例2 
核心内容 ---例3 
核心内容 ---例3 
核心内容 ---例3 
五、三个例子分析
? Example 1: 自己写测试框架
? Example 2: 用CppUnit改写Exp1
? Example 3:完整的例子 ,用HelperMacros 
Example 1: 自己写测试框架
? Unit Test Frameworks
? 步骤0:建立单元测试框架
? 步骤1:建立单元测试 (TDD第一次测试 )
? 步骤2:正确建立Book  (TDD编写一次)
? 步骤3:再次测试 (TDD第二次测试)
Exp1--- 0建立单元测试框架
Exp1--- 0建立单元测试框架
Exp1--- 0建立单元测试框架
Exp1--- 1建立单元测试 (Test 1st)
Exp1--- 1建立单元测试 (Test 1st)
Exp1--- 1建立单元测试 (Test 1st)
Exp1--- 3再次测试  (Test 2nd)
Example 2: 用CppUnit改写Exp1
? 1:使用CppUnit框架的TestCase替换自定义的UnitTest
? 2 :如果需要运行多个测试而不是放在单个runTest()中,则需要引入TestFixture
? 3 :使用 TestSuite, 把main()中的addTest()转移到 suite()中
? 4 :由于对每个测试类都要重复编写suite()静态函数,容易出错,所以使用Helper Macros来替换手工编写suite()静态函数,和注册函数
Exp2--- 1使用TestCase
Exp2--- 2使用TestFixture
Exp2--- 2使用TestFixture
Exp2--- 2使用TestFixture
Exp2--- 2使用TestFixture
Exp2--- 2使用TestFixture
Exp2--- 3使用TestSuite
Exp2--- 3使用TestSuite
Exp2--- 4使用HelperMacros
Exp2--- 4使用HelperMacros
Example 4:完整例子
? CppUnit Cookbook
? Example3-1
? Example3-2
六、CppUnit源码解读
? 参考“CppUnit源码解读.doc”
七、讨论