Posts Tagged ‘cppunit’

11
Mar

cppunit中的帮助宏

by huubby in C and CPP

本文来自include\cppunit\extensions\HelperMacros.h头文件,这里只是做简单的翻译,少数地方加入一些自己的理解。
cppunit cookbook里,我们了解了cppunit的使用方法,其中牵涉到cppunit预定义的几个宏,CPPUNIT_TEST_SUITE(),CPPUNIT_TEST(),CPPUNIT_TEST_SUITE_END()和CPPUNIT_TEST_SUITE_NAMED_REGISTRATION()。这些宏由cppunit提供,以帮助使用者更加方便的定义一个test suite。来具体看看这几个宏的作用。
class CTDemoMfc_CDemo : public CppUnit::TestCase{
            CPPUNIT_TEST_SUITE(CTDemoMfc_CDemo);
            CPPUNIT_TEST(TestAdd);
            CPPUNIT_TEST(TestSubtration);
            CPPUNIT_TEST_SUITE_END();
public:
	CTDemoMfc_CDemo(void);
	void TestAdd();
	void TestSubtration();
	static std::string RegCaseName() { return "CTDemoMfc_CDemo";}
public:
	~CTDemoMfc_CDemo(void);
};
这段代码里面,CPPUNIT_TEST_SUITE()和CPPUNIT_TEST_SUITE_END()两个宏的作用就是在类CTDemoMfc_CDemo中添加两个方法。一个辅助性的addTestsToSuite方法,添加测试项到test suite里面,另一个static CppUnit::TestSuite *suite()方法,返回一个指向由CPPUNIT_TEST定义的一系列test的指针。
在类CTDemoMfc_CDemo的实现文件里面,还有一个宏:CPPUNIT_TEST_SUITE_NAMED_REGISTRATION(),它创建了一个AutoRegisterSuite类的静态对象,自动将test suite注册到一个全局的registry中。这个全局的registry可以生成一个Test实例,包含了所有的已注册的test suite。
CPPUNIT_TEST_SUITE_NAMED_REGISTRATION(CTDemoMfc_CDemo,CTDemoMfc_CDemo::RegCaseName());
CppUnit::MfcUi::TestRunner runner;
CppUnit::TestFactoryRegistry &registry =
                 CppUnit::TestFactoryRegistry::getRegistry(CTDemoMfc_CDemo::RegCaseName());
runner.addTest(registry.makeTest());
runner.run();
test suite宏也可用于模板测试类
template<typename CharType>
class StringTest : public CppUnit::TestCase {
   CPPUNIT_TEST_SUITE( StringTest );
   CPPUNIT_TEST( testAppend );
   CPPUNIT_TEST_SUITE_END();
public:
   ...
};
在实现文件中添加代码:
CPPUNIT_TEST_SUITE_NAMED_REGISTRATION( StringTest<char> );
CPPUNIT_TEST_SUITE_NAMED_REGISTRATION( StringTest<wchar_t> );

Read the rest of this entry »

27
Jan

CppUnit CookBook中文版

by huubby in C and CPP

自己翻译的cppunit cookbook,如有错漏,欢迎指出。可以在这里下载到cppunit的最新版本源码。

这只是一个cookbook的翻译,并没介绍安装方法,你可以在这里找到win32下的安装方法和例子。不过,这个例子并不清楚,还是建议你看看这里的例子,清楚的多。看完这些安装方法和例子之后,再回头看看这篇cookbook,应该会帮助你理解例子里面那些代码的含义。

1、简单的测试案例

怎么才能知道你的代码是不是能够正常工作?有很多方法可以达到这个目的。通过调试器单步跟踪,或者在你的代码里加入打印输出代码是两个比较简单的办法,但是这两个方法都有缺点。单步跟踪不能自动进行,每次代码稍有调整就要进行调试。打印输出也不错,只是这种方法会增加很多不必要的代码,导致代码臃肿丑陋。

CppUnit单元测试很容易建立起来,并且可以自动进行,而且,一旦你写完测试用例,就能通过它们保证你的代码质量。

按照下面的流程可以构造一个简单的test:

1、继承CppUnit::TestCase类。

2、重写runTest()方法。

3、使用CPPUNIT_ASSERT()和CPPUNIT_ASSERT(bool)两个宏来检测表达式或值,以判断测试成功与否。

举个例子,如果要测试一个复数类的赋值(=号)运算符,按照上面的步骤,代码如下:

class ComplexNumberTest : public CppUnit::TestCase
{
public:
     ComplexNumberTest( std::string name ) : CppUnit::TestCase( name ) {}
     void runTest()
     {
          CPPUNIT_ASSERT( Complex (10, 1) == Complex (10, 1) );
          CPPUNIT_ASSERT( !(Complex (1, 1) == Complex (2, 2)) );
     }
};

一个简单的test就建起来了。但通常来说,我们会在同一个对象里面有很多小的测试用例。这种情况下,我们用fixture。

2、fixture

fixture是为一组测试用例提供基础服务的对象,当你边开发边测试时,使用fixture非常方便。我们试着模拟一下这种边开发边测试情况。

假设我们真的在开发一个复数类,首先,定义一个空的Complex类:

class Complex {};

现在,创建一个上面的ComplexNumberTest类对象,然后编译代码看看会发生什么。我们会得到几个编译错误。因为测试过程中用到了==操作符,但我们并没定义这个运算符。

现在为Complex类定义一个:

bool operator==( const Complex &amp;a, const Complex &amp;b)
{
     return true;
}

再次编译运行这个测试。这次编译虽然通过了,但测试却是失败的。

要再做一点点事情让==操作符正常工作,重写代码如下:

class Complex
{
     friend bool operator ==(const Complex&amp; a, const Complex&amp; b);
     double real, imaginary;
public:
     Complex( double r, double i = 0 ) :real(r) , imaginary(i) {}
};
bool operator ==( const Complex &amp;a, const Complex &amp;b )
{
     return a.real == b.real  &amp;&amp;  a.imaginary == b.imaginary;
}

编译运行,测试顺利通过。

好了,现在我们准备增加一些新的操作和新的测试。这个时候,fixture就体现出方便性了。因为在这个时候,如果使用fixture实例化三四个Complex对象,并且在测试过程中复用这几个对象,这样不需要重复构建这些实例对象,测试代码会更加好写。

fixture的使用步骤如下:

- 给每个fixture添加成员变量;

- 重写CppUnit::TestFixture::setUp()以初始化这些变量;

- 重写CppUnit::TestFixture::tearDown()方法释放setUp申请的系统资源。

class ComplexNumberTest : public CppUnit::TestFixture
{
private:
     Complex *m_10_1, *m_1_1, *m_11_2;
public:
    void setUp()
    {
         m_10_1 = new Complex( 10, 1 );
         m_1_1 = new Complex( 1, 1 );
         m_11_2 = new Complex( 11, 2 );
    }
    void tearDown()
    {
         delete m_10_1;
         delete m_1_1;
         delete m_11_2;
     }
};

有了这个fixture之后,我们就能在开发过程中添加另外的测试用例和我们需要的其他东西了。

Read the rest of this entry »