undefined

1. Boost.Test

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
#define BOOST_TEST_MAIN
#include <boost/test/unit_test.hpp>
#include <stdexcept>

void test(int n) {
if (n == 42) {
return ;
}
throw std::runtime_error("Not the answer");
}

BOOST_AUTO_TEST_CASE(my_test) {
BOOST_TEST_MESSAGE("Testing");
BOOST_TEST(1+1 == 2);
BOOST_CHECK_THROW(test(41), std::runtime_error);
BOOST_CHECK_NO_THROW(test(42));

int expected = 5;
BOOST_TEST(2+2 == expected);
BOOST_CHECK(2+2 == expected);
BOOST_REQUIRE(1+1 == 3);
BOOST_TEST_MESSAGE("no execute");
}

BOOST_AUTO_TEST_CASE(null_test) {}

编译:GCC:g++ -DBOOST_TEST_DYN_LINK test.cpp -lboost_unit_test_framework

  • 我们在包含单元测试的头文件之前定义了 BOOST_TEST_MAIN。如果编译时用到了多个源文件,只有一个应该定义该宏。多文件测试的时候,我一般会考虑把这个定义这个宏加包含放在一个单独的文件里(只有两行)

  • 我们用 BOOST_AUTO_TEST_CASE 来定义一个测试用例。一个测试用例里应当有多个测试语句(如 BOOST_CHECK)

  • 我们用 BOOST_CHECK 或 BOOST_TEST 来检查一个应当成立的布尔表达式。在测试失败时,执行仍然会继续。BOOST_TEST 可以利用模板技巧来输出表达式的具体内容。但在某些情况下,BOOST_TEST 试图输出表达式的内容会导致编译出错,这时可以改用简单的 BOOST_CHECK。

  • 我们用 BOOST_CHECK_THROW 和 BOOST_CHECK_NO_THROW 来检查一个应当抛出异常和不应当抛出异常的语句。

  • 使用 BOOST_REQUIRE 和 BOOST_TEST_REQUIRE 表达式一旦失败,整个测试用例就会停止执行,但其他测试用例仍会正常执行。

  • 缺省情况下单元测试的输出只包含错误信息和结果摘要,但输出的详细程度是可以通过命令行选项来控制。在运行测试程序时加上参数 --log_level=all(或 -l all),就可以看到更加详尽的输出

Boost.Test产生的可执行代码支持很多命令行参数,可以用–help命令行选项来查看,常用的有:

  • build_info 可用来展示构建信息
  • color_output 可用来打开或关闭输出中的色彩
  • log_format 可用来指定日志输出的格式,包括纯文本、XML、JUnit等
  • log_level 可指定日志输出的级别,有all、test_suite、error、fatal_error、nothing 等一共11个级别
  • run_test 可选择只运行指定的测试用例
  • show_progress 可在测试时显示进度,在测试数量较大时比较有用

2. Catch2

独有的优点:

  • 只需要单个头文件即可使用,不需要安装和链接,简单方便
  • 可选使用BDD(Behavior-Driven Development)风格的分节形式
  • 测试失败可选直接进入调试器(windows和macOS上)

下载头文件:https://github.com/catchorg/Catch2/blob/v2.x/single_include/catch2/catch.hpp 直接引入到代码中,是分支 v2.x 分支的

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
#define CATCH_CONFIG_MAIN
#include <catch2/catch.hpp>
#include <stdexcept>

void test(int n) {
if (n == 42) {
return ;
}
throw std::runtime_error("Not the answer");
}

TEST_CASE("My first test", "[my]") {
INFO("Testing");
CHECK(1+1 == 2);
CHECK_THROWS_AS(test(41), std::runtime_error);
CHECK_NOTHROW(test(42));

int expected = 5;
CHECK(2+2 == expected);
}

直接编译 g++ test.cpp -o test 即可

BDD风格的测试:一般采用这样的结构

  • Scenario:场景,我要做某某事
  • Given:给定,已有的条件
  • When:当,某个事件发生时
  • Then:那样,就应该发生什么

如下,要测试一个容器,代码如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
SCENARIO("Int container can be accessed and modified",
"[container]")
{
GIVEN("A container with initialized items")
{
IntContainer c{1, 2, 3, 4, 5};
REQUIRE(c.size() == 5);

WHEN("I access existing items")
{
THEN("The items can be retrieved intact")
{
CHECK(c[0] == 1);
CHECK(c[1] == 2);
CHECK(c[2] == 3);
CHECK(c[3] == 4);
CHECK(c[4] == 5);
}
}

WHEN("I modify items")
{
c[1] = -2;
c[3] = -4;

THEN("Only modified items are changed")
{
CHECK(c[0] == 1);
CHECK(c[1] == -2);
CHECK(c[2] == 3);
CHECK(c[3] == -4);
CHECK(c[4] == 5);
}
}
}
}

Catch2的分离编译

catch2 的编译速度慢,原因主要是构建 catch2 的主程序部分,而这部分在项目中只需要做一次,以后不会再有变动。因此需要分离如下这部分代码在主程序里:

1
2
#define CATCH_CONFIG_MAIN
#include "catch.hpp"

只要这两行,来单独编译 Catch2 的主程序部分。你的实际测试代码里,则不要再定义 CATCH_CONFIG_MAIN 了。你会发现,这样一分离后,编译速度会大大加快。