1,常用的C++单元测试框架
测试驱动开发(TDD)已经是一种非常流行的开发方式了
在java和net中都提供了非常好的单元测试框架
- CPPUnit:著名的XUnit系列产品之一,熟悉JUnit、NUnit的开发人员可以很快上手。
- CXXTest:需要进行预处理,需要安装Perl或Python。
- Boost Test:功能强大,提供了自动注册和手动注册两种方式,更重要的是来自千锤百炼的Boost库。
- Google Test:Google在去年开源的测试框架,据说其内部上千个项目均采用该框架进行单元测试。
先来看一个简单的Boost Test示例
2
3#include <boost/test/unit_test.hpp>
4
5
6
7int add(int i, int j);
8
9
10
11BOOST_AUTO_TEST_SUITE(minimal_test)
12
13
14
15BOOST_AUTO_TEST_CASE(my_test)
16
17{
18
19 BOOST_CHECK(add(2, 2) == 5);
20
21 BOOST_REQUIRE(add(2, 2) == 4);
22
23 if(add(2, 2) != 4)
24
25 BOOST_ERROR("oops!");
26
27 if(add(2, 2) != 4)
28
29 BOOST_FAIL("oops!");
30
31 if(add(2, 2) != 4)
32
33 throw "oops!";
34
35 BOOST_CHECK_MESSAGE(add(2, 2) == 4, "add(..) result: " << add(2, 2));
36
37 BOOST_CHECK_EQUAL(add(2, 2), 4);
38
39}
40
41
42
43BOOST_AUTO_TEST_SUITE_END()
44
45
首先,需要定义#define BOOST_TEST_MODULE example或者#define BOOST_AUTO_TEST_MAIN,
否则测试模块初始化函数需要手动实现;然后宏“BOOST_AUTO_TEST_SUITE(minimal_test)”将创建
一个名为minimal_test的测试套件,并将其加入到测试模块中。宏“BOOST_AUTO_TEST_CASE(my_test)”
将创建一个名为“my_test”的测试用例,并将其加入到测试套件minimal_test中。
关于测试模块常犯的一个错误是在不同的测试文件中定义不同的“#define BOOST_TEST_MODULE example”
,最后将导致“multiple definition of ‘init_unit_test_suite(int, char**)’”错误,原因是在一个测试程序中只允许
存在一个定义一个测试模块。测试模块中存在一个主测试套件,所有未明确包含到测试套件中的测试用例将被
包含到主测试套件中。
下面是具体的测试过程了,在程序中展示了七种不同的方式来对函数add进行测试:
-
BOOST_CHECK:这种方法将检查到的错误保存起来,测试结束时由测试框架自动显示;
-
BOOST_REQUIRE:同样是检查错误,与BOOST_CHECK不同之处在于如果发生错误时将抛出一个异常,因此后续的测试将不会继续进行;
-
BOOST_ERROR:可以用来对错误进行独立描述,测试结束时由测试框架自动显示;
-
BOOST_FAIL:同样可以用来对错误进行独立描述,调用时将抛出一个异常;
-
抛出异常方式:测试框架将捕获该异常并打印出错误信息;
-
BOOST_CHECK_MESSAGE:与BOOST_CHECK类似,可以在第二个参数中加入错误描述信息;
-
BOOST_CHECK_EQUAL:与BOOST_CHECKL类似,用来检查两个参数是否相等。
3,单参数测试用例
有时候需要对一个测试用例使用不同的参数运行测试,前面提到的BOOST_AUTO_TEST_CASE不能满足需求,
此时需要对测试用例进行手动注册(来自Boost文档):
#include <boost/test/included/unit_test.hpp>
#include <boost/test/parameterized_test.hpp>
using namespace boost::unit_test;
void free_test_function( int i )
{
BOOST_CHECK( i < 4 /* test assertion */ );
}
test_suite* init_unit_test_suite( int argc, char* argv[] )
{
int params[] = { 1, 2, 3, 4, 5 };
framework::master_test_suite().
add( BOOST_PARAM_TEST_CASE( &free_test_function, params, params+5 ) );
return 0;
}
4夹具(Fixture)
如果在多个测试用例中需要使用数据库连接,这时候要用到夹具来自动执行安装、清理过程。Boost Test采用RAII技术来实现夹具:
1 struct <fixture-name> {2
3 <fixture-name>();// 安装
4
5 ~<fixture-name>();// 拆卸
6
7};
2
3#include <boost/test/included/unit_test.hpp> //待测工程头文件
4
5
6
7struct F
8
9{
10
11 F() : i(0)
12
13 {
14
15 //std::cout << ("创建夹具") << std::endl;
16
17 }
18
19 ~F()
20
21 {
22
23 //std::cout << "销毁夹具" << std::endl;
24
25 }
26
27
28
29 int i;
30
31};
32
33
34
35BOOST_FIXTURE_TEST_SUITE(const_string_test, F)//测试套件
36
37
38
39BOOST_FIXTURE_TEST_CASE( test_case1, F )//测试用例
40
41{
42
43 BOOST_CHECK( i == 1 );
44
45 ++i;
46
47}
48
49
50
51BOOST_FIXTURE_TEST_CASE( test_case2 )
52
53{
54
55 BOOST_CHECK_EQUAL( i, 1 );
56
57}
58
59
60
61BOOST_AUTO_TEST_CASE( test_case3 )
62
63{
64
65 BOOST_CHECK( true );
66
67}
68
69
70
71BOOST_AUTO_TEST_SUITE_END()
72
73
25 // TODO: Your test code here 26 BOOST_WARN(tmpTested->add(2, 2) == 4); // WARN型预言检测 27 BOOST_CHECK(tmpTested->add(2, 2) == 4); // CHECK型预言检测 28 BOOST_REQUIRE(tmpTested->add(2, 2) == 4); // REQUIRE型预言检测
使用宏BOOST_FIXTURE_TEST_SUITE在第二个参数中指定夹具来代替宏BOOST_AUTO_TEST_SUITE以建立
测试套件,夹具将在该测试套件的所有测试用例中可用。使用宏BOOST_FIXTURE_TEST_CASE代替
宏BOOST_AUTO_TEST_CASE建立测试用例,可以在该测试用例中使用夹具。需要注意的是在每一个
测试用例中都将会执行夹具的安装和卸载过程。
5,测试输出
Boost Test中包括了十个级别的日志信息:
- 成功信息
- 测试树往返移动通知
- 通用信息
- 警告信息
- 非致命错误信息
- 未捕获C++异常
- 致命系统错误
- 所有信息
- 无信息
以上级别以由低到高的级别排列,可以在运行时参数log_level中设置,还可以使用参数log_format来制定输出格式。
可以向测试程序传递参数来定制测试结果,如“test.exe –build_info=yes –log_level=all”下面是常用的测试参数:
-
build_infoa:设置为yes将在开始运行测试前打印当前的操作系统版本、编译器版本等信息;
-
log_level:对应于十个日志级别,包括all、success、test_suite、message、warning、error、cpp_exception、system_error、fatal_error、nothing。
-
output_format:定义日志的输出格式。目前支持两种格式,HRF(可读格式)和XML格式;
-
run_test:指定要运行的测试单元,包括测试套件与测试用例,可以使用通配符“*”来制定运行符合特定条件的测试单元,如“test.exe –run_test=*/test1”将运行所有名为test1并位于主套件直接子套件的测试单元;
-
show_progress:设置为yes将在运行测试时显示当前的进度。