Boost C++测试组件test库

锺离宸
2023-12-01

1 boost::test简介和基本概念

test库用于单元测试命令行测试组件,全称Unit Test Framework(UTF),优点很多,可以胜任不同强度的测试功能

1.1 基本概念

  1. 一个测试模块大致分为四部:测试安装测试主体测试清理测试运行器
  2. 测试主体是实际运行部分,通常包含多个测试套件,而测试套件又由多个测试用例组成
  3. 测试套件测试用例组合成测试树,根节点称为主测试套件。进而可以理解以主测试套件为根节点,测试套件为非叶子节点,测试用例为叶子节点整个主体

注意事项:
1. 主测试套件必须要有,用BOOST_TEST_MAIN或者BOOST_TEST_MODULE,而且必须位于头文件#include <boost/test/unit_test.hpp>之前!!!

2 测试模块

2.1 测试用例 BOOST_AUTO_TEST_CASE

测试用例作为最小单元,各个测试用例之间是无关的,发生错误对其他测试用例没有影响,其内部可以包含多个测试断言函数

BOOST_AUTO_TEST_CASE(test_name)

注意:测试用例以t开头

2.2 测试套件 BOOST_AUTO_TEST_SUITE

测试套件包含一个或多个测试用例

BOOST_AUTO_TEST_SUITE( suite_name)	// 开始
BOOST_AUTO_TEST_SUITE_END()			// 结束

注意:

  1. 上面两句必须成对出现,加在之间的属于该测试套件
  2. 测试套件以s开头

2.3 测试案例

下面用一个Boost的智能指针作为案例,并且预设一个错误,如下
这个案例,有一个测试套件,且该测试套件有2个测试用例,当然根节点必须在开头

如何编译运行?

  1. 显然下面没有main函数,直接编译链接肯定会出错
  2. test库必须依赖两个动态库,分别是boost_unit_test_frameworkboost_test_exec_monitor
  3. 使用test库需要提前编译安装好boost库!!!

编译命令

g++ -o xxx xxx.cpp -std=c++11 -lboost_unit_test_framework -lboost_test_exec_monitor
  1. boost_unit_test_framework : 表示测试框架库,包含所有的测试宏
  2. boost_test_exec_monitor : 测试文件中不需要main,因此要专门链接进执行监视库,这个库是静态库。

-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"

注意:我们从结果可以知道,错误数量,位置,错误原因。

2.4 测试安装、测试清理

测试安装、测试清理可以类比类的构造函数、析构函数

  1. 测试安装:初始化测试用例或者测试套件所需的数据
  2. 测试清理:执行必要的清理工作

因此,我们很容易想到类,很符合此时的需求 ,但是这里需要主要下面的事项
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库,包含测试安装测试主体–测试套件/测试用例测试清理

3 命令行–运行参数

对生成的二进制文件,直接查看帮助,获得帮助

./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

4 测试装饰器

为测试套件和测试用例添加具体的修饰,比如添加标签label描述信息description等信息
有两种方式

  1. 使用宏 BOOST_TEST_DECORATOR()
  2. 直接在测试套件、测试用例中添加

注意:

  1. 上面提及的附加信息,全部在boost::unit_test
  2. 添加的信息没有逗号隔开!!!
#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"

5 测试泛型代码

如何测试模板类和模板函数?
答:使用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,判定是否相等

6 其他

6.1 如何忽略测试失败?

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

6.1 如何测试多个测试套件

有两个关键点
一、在主函数中,添加下面的头文件

  1. #include <boost/test/included/unit_test.hpp>
  2. 将需要测试源文件包含进来
#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

6.3 关于宏名的含义

很清晰,看见宏名你就能猜中八成,

  1. boost库中所有的宏名都是以BOOST_开头

下面是具体细节,对于宏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)		仅用于输出信息	
 类似资料: