Loading

轻松使用Boost.test单元测试框架

Boost.test单元测试框架

Boost.test提供了一套方便进行单元测试的框架,由于不是仅头文件的库,使用时需要链接一下相关的库。

find_package(Boost REQUIRED COMPONENTS system unit_test_framework test_exec_monitor CONFIG)

target_link_libraries(demo 
    PRIVATE Boost::unit_test_framework 
    PRIVATE Boost::test_exec_monitor
)

常使用下列头文件:

#include <boost/test/unit_test.hpp>
#include <boost/test/unit_test_suite.hpp>
#include <boost/test/data/test_case.hpp>
#include <boost/test/results_collector.hpp>

在每个测试的cpp文件最开始需要使用宏BOOST_TEST_MODULE定义一个模块名称,比如

#define BOOST_TEST_MODULE MyTestModule
...

Boost.test依赖这个宏识别不同的测试模块和控制测试套件的自动注册与执行。

测试用例

Boost的单元测试提供了BOOST_AUTO_TEST_CASE用于开启单元测试,使用方法如下:

BOOST_AUTO_TEST_CASE(my_test_case) {
    int a = 1 + 1;
    BOOST_TEST(a == 2);
}

测试套件

Boost.test使用BOOST_AUTO_TEST_SUITE(my_suite)开启一个测试套件,使用BOOST_AUTO_TEST_SUITE_END()标识一个测试套件结束。套件的测试用例写在一组宏的中间

OOST_AUTO_TEST_SUITE(my_suite)

BOOST_AUTO_TEST_CASE(condition1) {
    BOOST_TEST(2 * 3 == 6);
}

BOOST_AUTO_TEST_CASE(condition2) {
    BOOST_TEST(3 - 2 == 1);
}

BOOST_AUTO_TEST_SUITE_END()

测试夹具

Boost.test支持测试夹具,即可以声明一个类,测试用例可以直接使用类中的方法和成员变量。Boost.test支持三类测试夹具,全局夹具,套件夹具和用例夹具:

  • 全局夹具:全局夹具只会在程序开始和结束时被执行一次,可以用于程序数据的初始化和释放,初始化的逻辑写在全局夹具的构造函数中,释放逻辑写在全局夹具的析构函数中。使用宏BOOST_GLOBAL_FIXTURE注册全局夹具;
  • 用例夹具:用例夹具就是仅供单个测试用例使用的夹具,在用例开始时构造,在用例结束时析构,使用宏BOOST_FIXTURE_TEST_CASE声明,第一个参数是用例名称,第二个参数是夹具类;
  • 套件夹具:套件夹具可以适用于套件中的所有用例。在每个用例开始时构造,在用例结束时析构,使用宏BOOST_FIXTURE_TEST_SUITE绑定套件和夹具类,然后和普通测试套件一样,使用宏BOOST_AUTO_TEST_SUITE_END标识套件结束,套件中的每个用例可以使用宏BOOST_AUTO_TEST_CASE定义,不需要再指定夹具,会默认绑定。
struct MyGlobFixture {
    int number;

    MyGlobFixture() : number(42) { std::cout << "myglobfixture construct\n"; }

    ~MyGlobFixture() { std::cout << "myglobfixture destory\n"; }
};

BOOST_GLOBAL_FIXTURE(MyGlobFixture);

struct MyCaseFixture {
    int value;

    MyCaseFixture() : value(42) { std::cout << "mygcasefixture construct\n"; }

    ~MyCaseFixture() { std::cout << "mycasefixture destory\n"; }
};

BOOST_FIXTURE_TEST_CASE(fixture_case, MyCaseFixture) {
    BOOST_CHECK(value == 42);
}

struct MyFixture {
    int value;

    MyFixture() : value(42) { std::cout << "myfixture construct\n"; }

    ~MyFixture() { std::cout << "myfixture destory\n"; }
};

BOOST_FIXTURE_TEST_SUITE(my_fixture_test_suite, MyFixture)

BOOST_AUTO_TEST_CASE(condition1) {
    BOOST_TEST(value == 42);
}

BOOST_AUTO_TEST_CASE(condition2) {
    value = 13;
    BOOST_TEST(value == 13);
}

BOOST_AUTO_TEST_SUITE_END();

数据驱动测试(参数化测试)

Boost.test支持数据驱动的测试模式(参数化测试),使用BOOST_DATA_TEST_CASE宏定义一个参数化测试用例,支持在第二个参数中传入vectortuple等容器,程序在执行时,会为容器中的每个元素创建一个测试用例。

BOOST_DATA_TEST_CASE(my_data_test, boost::unit_test::data::xrange(1, 5), x) {
    BOOST_CHECK(x + x == 2 * x);
}

BOOST_DATA_TEST_CASE_F(MyFixture, my_data_test_fixture, boost::unit_test::data::make({1, 2, 3}),
                       x) {
    BOOST_TEST(value + x == 42 + x);
}

BOOST_DATA_TEST_CASE(my_complex_data_test,
                     boost::unit_test::data::make(std::make_tuple(1, "apple"),
                                                  std::make_tuple(2, "banana"),
                                                  std::make_tuple(3, "cherry")),
                     seq, fruit) {
    BOOST_CHECK(seq < 2 || fruit == "cherry");
}

Boost.test在boost::unit_test::data命名空间中提供了一个make方法,方便数据驱动测试用例的编写。
所有的数据驱动测试用例都可以被嵌套在套件中使用。

自定义测试观察者

Boost.test中使用观察者模式获取测试结果数据,默认定义了一个单例的results_collector类,如果需要自定义测试结果数据处理的话,就需要写一个boost::unit_test::test_observer派生类。

#define BOOST_TEST_NO_MAIN

class MyObserver : public boost::unit_test::test_observer {
public:
    void test_unit_finish(boost::unit_test::test_unit const& test, unsigned long elapsed) override {
        const auto& result = boost::unit_test::results_collector.results(test.p_id);
        std::cout << "Test Unit Suite Name: " << test.p_name.value << " | "
                  << "Time: " << elapsed << " | Status: "
                  << (result.result_code() == boost::exit_success ? "pass" : "failure")
                  << std::endl;
    }
};

bool init_test() {
    static MyObserver myobserver;
    boost::unit_test::framework::register_observer(myobserver);
    return true;
}

int main(int argc, char** argv) {
    return boost::unit_test::unit_test_main(init_test, argc, argv);
}

如果需要使用自定义的观察者,就得自己重新定义main函数,在main函数中调用boost::unit_test::unit_test_main初始化测试框架,这个函数需要传入一个自定义的初始化函数init_test,我们需要在这个函数中调用框架的方法boost::unit_test::framework::register_observer将自定义的观察者注册到框架中。
另外,如果自己编写main函数,需要在所有cpp文件的最开头定义BOOST_TEST_NO_MAIN宏,否则Boost.test会查找到自己的main函数,编译时报重定义错误。

输出测试报告

使用Boost.test框架的单元测试程序,可以在程序启动时添加--report_level=all,--log_format=XML,--log_sink=test_result.xml参数,用于控制单元测试报告的输出:

  • report_level: 控制输出报告的日志等级,可设置为all、detailed、short、no;
  • log_format: 控制输出报告的格式,支持XML、HRF和JUNIT三种格式;
  • log_sink: 指定报告输出位置。

虽然Boost.test本身不支持代码覆盖率统计,但可以结合Gcov/Lcov工具实现,在编译时添加-fprofile-arcs -ftest-coverage选项,然后运行测试完通过扩展工具生成覆盖率报告。

posted @ 2025-04-03 13:24  cwtxx  阅读(160)  评论(0)    收藏  举报