短小精悍的C++单元测试框架CppUnitLite源码分析
这几天集中精力阅读2个C++的测试框架源代码,一个是CppUnit,另一个就是今天要说的CppUnitLite啦。
CppUnitLite是一个由Michael Feathers开发的短小精悍的C++测试框架,Michael Feathers同时也是CppUnit的原作者。使用者可以通过这个框架根据自己的实际需要进行扩展。
- http://www.objectmentor.com/resources/downloads.html
- http://www.objectmentor.com/resources/bin/CppUnitLite.zip
这个框架的目的在于:
- 为开发者提供容易编写的独立的测试。
- 遵循JavaUnit的模式,避免使用C++的一些高级特性,比如RTTI,异常,模板等,这些因素不利于程序的移植。
现在假设有一个Deque类需要测试,测试所用的代码如下:
#include "TestHarness.h"
#include "Deque.h"
//from http://www.cnblogs.com/gpcuster
int main()
{
TestResult tr;
TestRegistry::runAllTests(tr);
return 0;
}
TEST( Deque, construction)
{
Deque d;
CHECK ( 0 == d.size() );
}
TEST (Deque, dont_try_suicide)
{
Deque d;
d.push_front(1);
d = d;
CHECK( 1 == d.pop_front());
}
tr是用来保存测试的结果的,你可以通过继承TestResult来添加自己需要,比如显示成功的测试有多少,失败的测试有多少。
TestRegistry::runAllTests(tr);这段代码表示开始执行测试。朋友们一定就会问啦,需要测试的代码是怎么关联进去的?要回答这个问题,我们可以看看实际要测试的代码,比如
TEST( Deque, construction)
{
Deque d;
CHECK ( 0 == d.size() );
}
这个函数看起来很奇怪,其实TEST是一个宏定义,这个框架中定义啦一个Test的基类,作者使用啦一个非常巧妙的宏(TEST)来生成一个派生与Test的类型,类型的名称由这个看似函数而不是函数的第一个参数(Deque)和第二个参数(construction)组成。同时实现啦Test::run函数,这里的实现也就是
{
Deque d;
CHECK ( 0 == d.size() );
}
其中的CHECK ( 0 == d.size() );也是用宏实现的。
为了能让大家看得更清楚,我们看看
TEST( Deque, construction)
{
Deque d;
CHECK ( 0 == d.size() );
}
这段代码在编译的时候变成了这个样子:
class constructionDequeTest : public Test
{
public:
constructionDequeTest () : Test ("Deque" "Test") {} \
void run (TestResult& result_);
}constructionDequeInstance;
void constructionDequeTest::run (TestResult& result_)
{
Deque d;
//CHECK ( 0 == d.size() );
{
if (!(0 == d.size()))
{
result_.addFailure (Failure (name_, __FILE__,__LINE__, "0 == d.size()"));
return;
}
}
}
这样,在编译的时候就自动生成啦一个用于测试类。
注意类定义的最后同时进行了实例化constructionDequeInstance,通过查看Test的代码你会发现,在实例话的时候调用了TestRegistry::addTest (this);这样,就把这个测试类的代码添加到了TestRegistry中去,同时TestRegistry又实现啦Singleton Pattern单件模式,所以我们也就很自然地回答了开始的问题(TestRegistry::runAllTests(tr);这段代码表示开始执行测试。朋友们一定就会问啦,需要测试的代码是怎么关联进去的?)。
如果大家有兴趣,可以进一步看一看Google公布的开源c++单元测试框架googletest
Google's framework for writing C++ tests on a variety of platforms (Linux, Mac OS X, Windows, Cygwin, Windows CE, and Symbian). Based on the xUnit architecture. Supports automatic test discovery, a rich set of assertions, user-defined assertions, death tests, fatal and non-fatal failures, value- and type-parameterized tests, various options for running the tests, and XML test report generation.