C++ (boost) 单元测试
C++
boost
单元测试
[TOC]
基本概念
Boost test库提供了一个用于单元测试的基于命令行界面的测试套件UTF
:Unit Test Framework,具有单元测试、检测内存泄露、监控程序运行的功能。
测试模块
- 测试安装
- 测试主体:测试主体是测试的模块的实际运行部分,由
测试用例
和测试套件
组成的测试树形成 - 测试清理
- 测试运行器
测试用例
测试用例是一个包含多个测试断言的函数;是可以被独立执行测试的最小单元。
测试套件
测试套件是测试用例的容器,可以嵌套,包含一个或多个测试用例,将多个测试用例分组管理,共享安装、清理代码。
测试夹具(test fixture)
测试安装和测试清理好比c++中的构造函数和析构函数,“测试夹具”实现了自动的测试安装和测试清理。
超轻量及测试lightweight_test
在头文件
中定义,他提供三个最简单的测试断言
BOOST_TEST //相当于BOOST_CHECK,断言表达式成立
BOOST_ERROR //直接断言失败,输出一条错误信息
BOOST_TEST_EQ //相当于BOOST_CHECK_EQUAL,断言两个表达式相等
Boost最小化测试套件 minimal test
boost提供的最简单的测试套件,有基本的测试断言,适合简单测试
该套件在头文件中
#include
定义,使用时只包含这个头文件就可以了,不需要链接其他的库,非常方便。在这个头文件中已经定义了一个mian()函数,我们不需要在写main()函数了,只需要实现一个test_main(int argc, char *argv[])
就可以了。这个头文件中定义了四个断言宏,可以供我们使用。
BOOST_CHECK(predicate) //断言表达式通过,如不通过不影响程序继续执行
BOOST_REQUIRE(predicate) //断言表达式必须通过,如不通过程序终止
BOOST_ERROR(message) // 给出一个错误信息,程序继续执行
BOOST_FAIL(message) // 给出一个错误信息,程序终止执行
下面是一个小栗子:
#include "boost/test/minimal.hpp"
#include "boost/format.hpp"
#include "iostream"
int test_main(int argc, char *argv[]) // 测试主函数,不需要在定义main()
{
using namespace boost;
format fmt("%d-%d");
BOOST_CHECK(fmt.size() == 0); // 验证fmt对象初始化,不通过继续执行
fmt % 12 % 34;
BOOST_REQUIRE(fmt.str() == "12-34"); // 验证结果,不通过则不予执行
BOOST_ERROR("演示一条错误信息"); // 打印一条错误信息,继续执行
fmt.clear();
fmt % 12;
try {
std::cout << fmt;
}
catch (...) {
BOOST_FAIL("致命错误,测试终止"); // 给出一个错误信息,终止执行
}
return 0;
}
程序输出:
boost_test.cpp(9): test fmt.size() == 0 failed in function: 'int test_main(int, char **)'
boost_test.cpp(12): 演示一条错误信息 in function: 'int test_main(int, char **)'
boost_test.cpp(20): 致命错误,测试终止 in function: 'int test_main(int, char **)'
总结,minimal test方便简单,但功能有限,适用于单元测试的演示,或者较小的程序段。
UTF测试断言
UTF中的测试断言:BOOST_LEVEL_ITEM
LEVEL | WARN | 警告级,不增加错误数量,不影响程序运行 |
---|---|---|
CHECK | 检查级别,增加错误数量,不影响程序运行 | |
REQUIRE | 最高级别,增加错误数量,程序终止运行 | |
ITEM | EQUAL/CLOSE | 测试相等性 |
GE/GT/LT/LE/NE | 测试不等性 | |
(NO_)THROW | 是否抛出异常 | |
MESSAGE | 测试信息 | |
... | ... |
组合出来就是如
BOOST_CHECK_EQUAL(l, r)
BOOST_REQUIRE_GE(l, r)
测试实例
举个栗子:
#define BOOST_TEST_MAIN
#define BOOST_TEST_DYN_LINK
#include
#include
using namespace boost;
// 开始测试套件s_smart_ptr
BOOST_AUTO_TEST_SUITE(s_smart_ptr)
// 测试用例1:t_scoped_ptr
BOOST_AUTO_TEST_CASE(t_scoped_ptr)
{
scoped_ptr p(new int(874));
BOOST_CHECK(p); // 测试指针p的可用性
BOOST_CHECK_EQUAL(*p, 875); // 测试p解引用的值
p.reset();
BOOST_CHECK(p == 0); // 测试p为空
}
// 测试用例2:t_shared_ptr
BOOST_AUTO_TEST_CASE(t_shared_ptr)
{
shared_ptr p(new int(100));
BOOST_CHECK(p);
BOOST_CHECK_EQUAL(*p, 100);
BOOST_CHECK_EQUAL(p.use_count(), 1);
shared_ptr p2 = p;
BOOST_CHECK_EQUAL(p, p2);
BOOST_CHECK_EQUAL(p2.use_count(), 2);
*p2 = 255;
BOOST_CHECK_EQUAL(*p, 255);
BOOST_CHECK_GT(*p, 200);
}
// 结束测试套件
BOOST_AUTO_TEST_SUITE_END()
关于#define BOOST_TEST_DYN_LINK
,如果不加的话会出现下面的错误:
Undefined symbols for architecture x86_64:
"_main", referenced from:
implicit entry/start for main executable
ld: symbol(s) not found for architecture x86_64
clang: error: linker command failed with exit code 1 (use -v to see invocation)
编译时同时需要链接-lboost_unit_test_framework
测试夹具
前面说测试夹具就好比c++的构造函数和析构函数,也可以理解为在执行测试之前设置一个环境,在测试完成时清除它。
看两个栗子吧:
#define BOOST_TEST_MODULE example
#include
#include
struct F {
F():i(0) { std::cout << "setup" << std::endl; }
~F() { std::cout << "teardown" << std::endl; }
int i;
};
BOOST_AUTO_TEST_SUITE( test )
BOOST_FIXTURE_TEST_CASE( test_case1, F ) {
BOOST_CHECK( i == 1 );
++i;
}
BOOST_AUTO_TEST_CASE( test_case2 ) {
BOOST_REQUIRE( 2 > 1 );
}
BOOST_AUTO_TEST_CASE( test_case3 ) {
int i = 1;
BOOST_CHECK_EQUAL( i, 1 );
++i;
}
BOOST_AUTO_TEST_SUITE_END()
输出结果是:
Running 3 test cases...
setup
boost_test.cpp:22: error in "test_case1": check i == 1 failed
teardown
*** 1 failure detected in test suite "Master Test Suite"
另一个栗子:
#define BOOST_TEST_MAIN
#define BOOST_TEST_DYN_LINK
#include
#include
#include
using namespace std;
using namespace boost;
// 全局测试夹具类
struct global_fixture {
global_fixture() {cout << "global setup" << endl;}
~global_fixture() {cout << "global teardown" << endl;}
};
// 定义全局夹具
BOOST_GLOBAL_FIXTURE(global_fixture);
// 测试套件夹具类
struct assign_fixture {
assign_fixture() {cout << "suit setup" << endl;}
~assign_fixture() {cout << "suit teardown" << endl;}
std::vector v;
};
// 定义测试套件级别的夹具
BOOST_FIXTURE_TEST_SUITE(s_assign, assign_fixture)
BOOST_AUTO_TEST_CASE(t_assign1) {
using namespace boost::assign;
v += 1, 2, 3, 4;
BOOST_CHECK_EQUAL(v.size(), 4);
BOOST_CHECK_EQUAL(v[2], 3);
}
BOOST_AUTO_TEST_CASE(t_assign2) {
using namespace boost::assign;
push_back(v)(11)(22)(33);
BOOST_CHECK_EQUAL(v.empty(), false);
BOOST_CHECK_LT(v[0], v[1]);
}
BOOST_AUTO_TEST_SUITE_END()
后一个栗子的输出是
global setup
Running 2 test cases...
suit setup
suit teardown
suit setup
suit teardown
global teardown