test库用于单元测试
、命令行
测试组件,全称Unit Test Framework(UTF),优点很多,可以胜任不同强度的测试功能
注意事项:
1. 主测试套件必须要有,用BOOST_TEST_MAIN
或者BOOST_TEST_MODULE
,而且必须位于头文件#include <boost/test/unit_test.hpp>之前!!!
测试用例作为最小单元,各个测试用例之间是无关的,发生错误对其他测试用例没有影响,其内部可以包含多个测试断言函数
BOOST_AUTO_TEST_CASE(test_name)
注意:测试用例以t开头
测试套件包含一个或多个测试用例
BOOST_AUTO_TEST_SUITE( suite_name) // 开始
BOOST_AUTO_TEST_SUITE_END() // 结束
注意:
下面用一个Boost的智能指针作为案例,并且预设一个错误,如下
这个案例,有一个测试套件,且该测试套件有2个测试用例,当然根节点必须在开头
如何编译运行?
boost_unit_test_framework
和boost_test_exec_monitor
编译命令
g++ -o xxx xxx.cpp -std=c++11 -lboost_unit_test_framework -lboost_test_exec_monitor
-l链接时,优先考虑动态库,找不到会链接同名的静态库,若还没有,ERROR
#define BOOST_TEST_MAIN // 必须定义主测试套件,必须位于头文件之前
#include <boost/test/unit_test.hpp> // test的头文件
#include <boost/smart_ptr.hpp>
// 测试套件的开始
BOOST_AUTO_TEST_SUITE(s_smart_ptr)
// 测试用例1
BOOST_AUTO_TEST_CASE(t_scoped_ptr) // 测试用例1 t_scoped_ptr
{
boost::scoped_ptr<int> p(new int(874));
BOOST_CHECK(p);
BOOST_CHECK_EQUAL(*p, 874);
}
// 测试用例2
BOOST_AUTO_TEST_CASE(t_shared_ptr) // 测试用例2 t_shared_ptr
{
boost::shared_ptr<int> p(new int(100));
BOOST_CHECK(p);
BOOST_CHECK_EQUAL(*p, 100);
BOOST_CHECK_EQUAL(p.use_count(), 1);
boost::shared_ptr<int> p2 = p;
BOOST_CHECK_EQUAL(p2, p);
BOOST_CHECK_EQUAL(p.use_count(), 3); // 预设一个错误
}
BOOST_AUTO_TEST_SUITE_END()
Output ,编译器是GCC
Running 2 test cases...
/home/topeet/myBoost/chap6_assert/_6_4.cpp(27): error: in "s_smart_ptr/t_shared_ptr": check p.use_count() == 3 has failed [2 != 3]
*** 1 failure is detected in the test module "Master Test Suite"
注意:我们从结果可以知道,错误数量,位置,错误原因。
测试安装、测试清理可以类比类的构造函数、析构函数
因此,我们很容易想到类,很符合此时的需求 ,但是这里需要主要下面的事项
1. 使用struct即可,不能用final修饰
2. 又称为测试夹具类,类比夹子的两边
3. 对于测试套件和测试用例,这里有两个宏
BOOST_FIXTURE_TEST_SUITE( suite_name, 测试夹具类) // 测试套件
BOOST_FIXTURE_TEST_CASE( test_name, 测试夹具类) // 测试案例
联想上面的根节点,这里也存在一个全局测试夹具,测试所有的套件,包括主测试套件
BOOST_TEST_GLOBAL_FIXTURE(测试夹具类) //全局测试夹具
#define BOOST_TEST_MAIN // 必须定义主测试套件,必须位于头文件之前
#include <boost/test/unit_test.hpp>
#include <boost/assign.hpp>
#include <vector>
struct global_fixture
{
global_fixture() { std::cout << "global setup\n"; }
~global_fixture() { std::cout << "global teardown\n"; }
};
BOOST_TEST_GLOBAL_FIXTURE(global_fixture); // 全局测试夹具
struct assign_fixture
{
assign_fixture() { std::cout << "suit setup\n"; }
~assign_fixture() { std::cout << "suit teardown\n"; }
std::vector<int> 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)
{
boost::assign::push_back(v)(10)(20)(30);
BOOST_CHECK_EQUAL(v.size(), 3);
BOOST_CHECK_EQUAL(v[2], 300); // 预设错误
}
BOOST_AUTO_TEST_SUITE_END()
输出:
两个测试用例,全局测试夹具已经生效位于开始和结尾处;测试套件夹具应用在每个测试用例上;其中第二个测试用例失败,并报告具体错误信息
Running 2 test cases...
global setup
suit setup
suit teardown
suit setup
/home/topeet/myBoost/chap6_assert/_6_4_4.cpp(36): error: in "s_assign/t_assign2": check v[2] == 300 has failed [30 != 300]
suit teardown
global teardown
*** 1 failure is detected in the test module "Master Test Suite"
上面,通过案例分析了如何使用boost库的test库,包含测试安装、测试主体–测试套件/测试用例、测试清理
对生成的二进制文件,直接查看帮助,获得帮助
./xxx --help
下面是几个参数的解释
run_test 指定需要运行的套件或者用例
build_info 输出编译器、系统等信息
output_format 输出格式,如HRF/XML
list_content 里出所有的测试套件和测试用例,不执行
show_progress 取值yes/no,显示完成的测试用例、总测试用例
对上面的案例,可以取如下运行参数
./xxx --build_info=yes --run_test=s_assign --output_format=HRF
输出:
Compiler: GNU C++ version 7.5.0
STL : GNU libstdc++ version 20191114
Boost : 1.74.0
global setup
suit setup
suit teardown
suit setup
suit teardown
为测试套件和测试用例添加具体的修饰,比如添加标签label、描述信息description等信息
有两种方式
注意:
#include "_6_1_other.cpp" // 其他源文件同理
#define BOOST_TEST_MAIN // 必须定义主测试套件,必须位于头文件之前
#include <boost/test/unit_test.hpp>
#include <boost/smart_ptr.hpp>
BOOST_AUTO_TEST_SUITE(s_decorator, *boost::unit_test::label("decorator"))
// 主要,添加的信息没有逗号隔开!!!
BOOST_AUTO_TEST_CASE(t_case1, *boost::unit_test::label("low") *boost::unit_test::description("a normal test case"))
{
}
BOOST_AUTO_TEST_CASE(t_case2, *boost::unit_test::label("low"))
{
}
// 允许1个断言失败
//BOOST_AUTO_TEST_CASE_EXPECTED_FAILURES(t_case3, 1)
BOOST_TEST_DECORATOR(*boost::unit_test::label("high"))
BOOST_AUTO_TEST_CASE(t_case3)
{
boost::scoped_ptr<int> p(new int(874));
BOOST_CHECK(p);
BOOST_CHECK_EQUAL(*p, 875);
}
BOOST_AUTO_TEST_SUITE_END()
输出,可以看出层次结构,一个测试套件下有三个测试用例,第一个测试用例有额外的描述信息
./xxx --list_content
s_decorator*
t_case1*: a normal test case
t_case2*
t_case3*
再熟悉下,run_test后面接的@label,这里面预设两个label,分别时low和high,其中low的测试用例绑定了两个,high只有一个
➜ chap6_assert git:(master) ✗ ./_6_4_7 --run_test=@low
Running 2 test cases...
*** No errors detected
➜ chap6_assert git:(master) ✗ ./_6_4_7 --run_test=@high
Running 1 test case...
/home/topeet/myBoost/chap6_assert/_6_4_7.cpp(25): error: in "s_decorator/t_case3": check *p == 875 has failed [874 != 875]
*** 1 failure is detected in the test module "Master Test Suite"
如何测试模板类和模板函数?
答:使用BOOST_AUTO_TEST_CASE_TEMPLATE
宏完成任务
BOOST_AUTO_TEST_CASE_TEMPLATE(test_name, type_name, TL)
注意:TL是什么呢?
使用自动测试方式,需要mpl的支持,就是在此基础上的一个容器
下面测试的是lexical_cast
,这个库相当于C语言的stoi,stod,,,,之类的,同时它还支持反向转换!!
#define BOOST_TEST_MAIN // 必须定义主测试套件,必须位于头文件之前
#include <boost/test/unit_test.hpp>
#include <boost/lexical_cast.hpp>
#include <boost/mpl/list.hpp>
BOOST_AUTO_TEST_SUITE(s_lexical_cast)
typedef boost::mpl::list<short, int, long> types;
BOOST_AUTO_TEST_CASE_TEMPLATE(T_lexical_cast, T, types)
{
T n(20);
BOOST_CHECK_EQUAL(boost::lexical_cast<std::string>(n), "20");
}
BOOST_AUTO_TEST_SUITE_END()
输出:
Running 3 test cases...
*** No errors detected
解释:
1. 其实,这里测试三个模板参数,分别是short, int, long,测试了三次,由于语法的限定,必须用mpl,这里用的list来盛放它们
2. 通过lexical_caststd::string将short(20)、int(20)、long(20)都转成字符串的20,判定是否相等
test库提供 BOOST_AUTO_TEST_CASE_EXPECTED_FAILURES
来忽略错误的最多次数
// 忽略n个错误
BOOST_AUTO_TEST_CASE_EXPECTED_FAILURES( test_name, n)
对于上面的代码,去掉对应的注释,输出结果如下
没有检测到错误,但是任然有错误信息,因为我们忽略了哈
Running 3 test cases...
/home/topeet/myBoost/chap6_assert/_6_4_7.cpp(25): error: in "s_decorator/t_case3": check *p == 875 has failed [874 != 875]
*** No errors detected
有两个关键点
一、在主函数中,添加下面的头文件
#define BOOST_TEST_MAIN // 必须定义主测试套件,必须位于头文件之前
#include <boost/test/included/unit_test.hpp>
#include "_6_1_other.cpp"
#include "_6_4_4.cpp"
#include "_6_4_7.cpp"
#include "_6_4_7_5.cpp"
//....更多的源文件
二、在被包含的文件中,修改成BOOST_TEST_INCLUDED
将BOOST_TEST_MAIN换成BOOST_TEST_INCLUDED
#define BOOST_TEST_INCLUDED
#include <boost/test/unit_test.hpp> // test的头文件
......
OK
很清晰,看见宏名你就能猜中八成,
下面是具体细节,对于宏BOOST_LVL_XXX
LVL:
WARN 警告,不影响程序运行、不增加错误数量
CHECK 检查,不影响程序运行、增加错误数量
REQUIRE 最高级别,断言失败,直接终止执行
XXX:
BOOST_LVL_EUQAL(L,R) L ?= R
类似有小于等于(GE)、等等
BOOST_LVL_THROW(e, msg) 失败,抛出异常
BOOST_LVL_NO_THROW(e) 失败,不抛出异常
BOOST_LVL_MESSAGE(e, msg) 失败,输出信息
BOOST_LVL_MESSAGE(msg) 仅用于输出信息