分类: C/C++
2011-03-18 15:09:26
一、 CppUnit简介
A、CppUnit源代码组成
CppUnit测试框架的源代码可以到
上下载,当前最高版本为cppunit-
图一
主要的文件夹有:
B、初识CppUnit测试环境
解压源代码包(cppunit-
1、进入example文件夹,用VC打开examples.dsw。我们先来看看CppUnit自带的测试例子。这些例子都是针对CppUnit自身的单元测试集,一方面这是CppUnit作者开发CppUnit框架过程中写的测试用例,另一方面,我们可以通过这些例子来学习如何在我们自己的工程中添加测试用例。
2、将CppUnitTestApp工程设为Active Project(Win32 Debug),编译后运行,则可以看到CppUnit的基于GUI方式进行单元测试TestRunner的界面。点击“Run”,将会看到如图二所示界面:
图二
这是一个针对CppUnit的单元测试结果,它表明刚才我们做了11个测试,全部通过。
点击“Browse”,我们还可以选择想要进行的单元测试,如图三:
图三
CppUnit将所有的单元测试按照树的结构来表示。在CppUnit中,最小的测试单元,称为TestMethod测试方法,而多个相关的测试方法又可以组成一个TestCase测试用例。多个测试用例又组成TestSuite测试包。测试包互相嵌套在一起,就形成了上面我们看到的树结构。我们可以选择其中任意的树节点来进行单元测试。
3、将CppUnitTestMain工程设置为Active Project(Win32 Debug),编译并运行,我们来看看另一个单元测试的环境,如图四:
图四
这是一个基于文本方式的单元测试环境。CppUnit提供了几种测试环境,一种基于文本,一种基于GUI,即图二。
4、将HostApp工程设置为Active Project(Win32 Debug),编译运行。如图五:
图五
这亦是一个对CppUnit自身进行的测试,只不过它向我们演示的是各种失败的测试。在基于GUI的测试环境中,若测试不成功,进度条显示红色,反之则为绿色。从测试结果我们可以看到失败的单元测试名称,引起测试不能通过的原因,以及测试失败的语句所在的文件及所在行数。
二、 CppUnit单元测试环境搭建
对CppUnit初步了解后,你是否已经磨拳擦掌,准备进行你的测试了?下面我们按照下面的步骤一步步完成我们自己的测试:
第一步:编译CppUnit 静态库文件*.lib和动态库文件*.dll:
1、 CppUnit的lib和dll
接下来进行编译工作。进入src文件夹,打开CppUnitLibraries.dsw。点击菜单build-batch build,编译成功的话,生成的库文件将被拷贝到lib目录下。中途或者会有些project编译失败,一般不用管它,我们重点看的是cppunit和TestRunner 这两个project的debug和release版本。
编译通过以后, 在lib/目录下,会生成若干lib,和dll文件, 都以cppunit开头. cppunitd表示debug版, cppunit表示release版。
CppUnit为我们提供了两套框架库,一个为静态的lib,一个为动态的dll。cppunit project:静态lib;cppunit_dll project:动态dll和lib。在开发中我们可以根据实际情况作出选择。
你也可以根据需要选择所需的项目进行编译,其中项目cppunit为静态库,cppunit_dll为动态库,生成的库文件为:
·
cppunit.lib:静态库release版;
·
cppunitd.lib:静态库debug版;
·
cppunit_dll.lib:动态库release版;
·
cppunitd_dll.lib:动态库debug版;
另外一个需要关注的project是TestRunner,它输出一个dll,提供了一个基于GUI 方式的测试环境,即前面我们提到的两种测试环境之一。我们也需要编译这个project,输出位置亦为lib文件夹。
为了方便开发,我们把这些编译出来的lib和dll拷贝到我们自己建立的一个文件夹中(当然你也可以不这么做),例如D:\Mytest\lib\,同时我们也把CppUnit源代码中include文件夹copy到我们自己的include文件夹下。
第二步:建立基于对话框的工程
打开VC,在File菜单项下选择New,建立基于dialog的工程。工程名Project name、存放位置Location可以自己决定,其他选项如下:
按OK确认后,进入如下界面。选择Dialog based选项,按Finish按钮后,一个空的基于对话框的工程就建立起来了。
第三步:添加被测对象CCounter。
将被测对象所在文件(*.h和*.cpp) 添加到工程中:
测试代码
//-----------------------------------------------------------Counter.h
#if
!defined(AFX_COUNTER_H__0A5AD14A_0E78_4976_9A70_A78649112BA5__INCLUDED_)
#define
AFX_COUNTER_H__0A5AD14A_0E78_4976_9A70_A78649112BA5__INCLUDED_
#if _MSC_VER
> 1000
#pragma once
#endif //
_MSC_VER > 1000
// Counter.h
: header file
//
#include
"Resource.h"
/////////////////////////////////////////////////////////////////////////////
// CCounter
dialog
class
CCounter : public CDialog
{
//
Construction
public:
CCounter(CWnd* pParent = NULL); // standard constructor
// Dialog
Data
//{{AFX_DATA(CCounter)
enum { IDD = IDD_DIALOG_COUNTER };
//}}AFX_DATA
int IsCodeLine(CString,int);
// Overrides
// ClassWizard generated virtual
function overrides
//{{AFX_VIRTUAL(CCounter)
protected:
virtual void
DoDataExchange(CDataExchange* pDX); //
DDX/DDV support
//}}AFX_VIRTUAL
//
Implementation
protected:
// Generated message map functions
//{{AFX_MSG(CCounter)
// NOTE: the ClassWizard
will add member functions here
//}}AFX_MSG
DECLARE_MESSAGE_MAP()
};
//{{AFX_INSERT_LOCATION}}
// Microsoft
Visual C++ will insert additional declarations immediately before the previous
line.
#endif //
!defined(AFX_COUNTER_H__0A5AD14A_0E78_4976_9A70_A78649112BA5__INCLUDED_)
//-----------------------------------------------------------Counter.cpp
#include
"stdafx.h"
#include
"MyTest.h"
#include
"Counter.h"
#ifdef _DEBUG
#define new
DEBUG_NEW
#undef
THIS_FILE
static char
THIS_FILE[] = __FILE__;
#endif
/////////////////////////////////////////////////////////////////////////////
// CCounter
dialog
CCounter::CCounter(CWnd*
pParent /*=NULL*/)
: CDialog(CCounter::IDD, pParent)
{
//{{AFX_DATA_INIT(CCounter)
// NOTE: the ClassWizard
will add member initialization here
//}}AFX_DATA_INIT
}
void
CCounter::DoDataExchange(CDataExchange* pDX)
{
CDialog::DoDataExchange(pDX);
//{{AFX_DATA_MAP(CCounter)
// NOTE: the ClassWizard
will add DDX and DDV calls here
//}}AFX_DATA_MAP
}
BEGIN_MESSAGE_MAP(CCounter,
CDialog)
//{{AFX_MSG_MAP(CCounter)
// NOTE: the ClassWizard
will add message map macros here
//}}AFX_MSG_MAP
END_MESSAGE_MAP()
/////////////////////////////////////////////////////////////////////////////
// CCounter
message handlers
int
CCounter::IsCodeLine(CString a,int b)
{
return 0;
}
第四步:屏蔽工程对话框
在工程MyTest.cpp文件中(本指南中为该文件名,实际学习时根据自己的工程文件名而变),找到BOOL CMyTestApp::InitInstance()方法,将如下蓝色代码注释掉:
BOOL
CMyTestApp::InitInstance()
{
AfxEnableControlContainer();
// Standard initialization
// If you are not using these
features and wish to reduce the size
//
of your final executable, you should remove from the following
//
the specific initialization routines you do not need.
#ifdef
_AFXDLL
Enable3dControls(); // Call this when
using MFC in a shared DLL
#else
Enable3dControlsStatic(); // Call this when linking to MFC
statically
#endif
/*
CMyTestDlg dlg;
m_pMainWnd = &dlg;
int nResponse = dlg.DoModal();
if (nResponse == IDOK)
{
// TODO: Place code here
to handle when the dialog is
// dismissed with OK
}
else if (nResponse == IDCANCEL)
{
// TODO: Place code here
to handle when the dialog is
// dismissed with Cancel
}
*/
// Since the dialog has been closed,
return FALSE so that we exit the
//
application, rather than start the application's message pump.
return FALSE;
}
由于我们希望这个Project运行后显示的是图2这样的CppUnit自带的界面,所以我们需要在Instance()中屏蔽掉原有的对话框(蓝色部分注释掉),代之以CppUnit的GUI。
第五步:实现CppUnit测试执行器,并将测试工厂添加到测试执行器中。
A、在BOOL CMyTestApp::InitInstance()中,添加如下红色代码:
BOOL
CMyTestApp::InitInstance()
{
AfxEnableControlContainer();
// Standard initialization
// If you are not using these
features and wish to reduce the size
//
of your final executable, you should remove from the following
//
the specific initialization routines you do not need.
#ifdef
_AFXDLL
Enable3dControls(); // Call this when
using MFC in a shared DLL
#else
Enable3dControlsStatic(); // Call this when linking to MFC
statically
#endif
//添加CppUnit的MFC类型的测试执行器
CppUnit::MfcUi::TestRunner runner;
//为被测试类(这里是CCounter)定义一个测试工厂(这里取名叫CounterTest):
CppUnit::TestFactoryRegistry
®istry
=
CppUnit::TestFactoryRegistry::getRegistry("CounterTest");
//并将工厂添加到测试执行器中
runner.addTest( registry.makeTest() );
//运行执行器,显示执行器GUI界面
runner.run();
/*
CMyTestDlg dlg;
m_pMainWnd = &dlg;
int nResponse = dlg.DoModal();
if (nResponse == IDOK)
{
// TODO: Place code here
to handle when the dialog is
// dismissed with OK
}
else if (nResponse == IDCANCEL)
{
// TODO: Place code here
to handle when the dialog is
// dismissed with Cancel
}
*/
// Since the dialog has been closed,
return FALSE so that we exit the
//
application, rather than start the application's message pump.
return FALSE;
}
B、由于在BOOL CMyTestApp::InitInstance()中引用了CppUnit的类,所以在文件开始处要添加如下头文件:
#include
#include
第六步:在工程中为被测对象CCounter编写测试类文件CounterTest(可以自定义文件名):
按照下面示图加入测试类的*.h文件和*.cpp文件:
CounterTest.h中的代码如下:
#include
"cppunit/extensions/HelperMacros.h"
class
IsCodeLineTest : public CppUnit::TestFixture {
// 声明一个TestSuite
CPPUNIT_TEST_SUITE( IsCodeLineTest);
// 添加测试用例到TestSuite, 定义新的测试用例需要在这儿声明一下
CPPUNIT_TEST(
Counter_UT_IsCodeLine_001);
// TestSuite声明完成
CPPUNIT_TEST_SUITE_END();
public:
// 定义测试用例
void Counter_UT_IsCodeLine_001();
};
CounterTest.cpp中的代码如下(注意头文件要做相应的修改):
#include
"stdafx.h"
#include
"CounterTest.h"
#include
".\counter\counter.h"
// 把这个TestSuite注册到名字为"CounterTest"的工厂中
CPPUNIT_TEST_SUITE_NAMED_REGISTRATION(
IsCodeLineTest,"CounterTest" );
#define
RET_OK 0
#define
RET_FAIL 1
void
IsCodeLineTest::Counter_UT_IsCodeLine_001()
{
//定义输入参数
int bIsComment;
CString
szFileLine;
//定义期望输出
int iOkReturn;
int iOkIsComment;
//定义测试实际输出
int iResult;
CCounter m_counter;
//用例输入
szFileLine = "int a";
bIsComment = false;
//期望输出
iOkReturn = RET_OK;
iOkIsComment = false;
//驱动被测函数
iResult =
m_counter.IsCodeLine(szFileLine,bIsComment);
//结果比较
CPPUNIT_ASSERT_EQUAL(iOkReturn,iResult);
CPPUNIT_ASSERT_EQUAL(iOkIsComment,bIsComment);
}
第七步:加入CppUnit 库文件:
把CppUnit相关的lib文件和dll文件(cppunitd.lib,cppunitd_dll.lib,testrunnerd.lib)加入到工程中:
第八步:设置头文件和lib库文件路径、打开RTTI开关、给dLL库设置环境变量:
A、
在VC的tools/options/directories/include files和library files中设置CppUnit include文件路径和lib文件路径:
B、
在你的VC project中打开RTTI开关。具体位置Project Settings/C++/C++
Language:
C、为TestRunnerd.dll设置环境变量
TestRunnerd.dll为我们提供了基于GUI的测试环境。为了让我们的测试程序能正确的调用它,需要把TestRunnerd.dll拷贝到你的工程路径下。或者最简单的方法是在操作系统的环境变量Path中添TestRunnerd.dll的路径,这样是最省事的。
第九步:编译执行。编译连接成功后,运行测试,出现下面的界面,表示测试用例Counter_UT_IsCodeLine_001运行成功.