[译]C++ Unit Testing With Boost.Test

原文为这篇文章,第一段觉得没什么用,就没有翻译。安装部分也有部分没有翻译,其余基 本都翻译了。

安装

在 Linux 下最简单的方式是使用包管理系统,Ubuntu 下可以使用如下命令:

1
sudo apt install libboost-test-dev

这种方法的缺点就是版本可能有些老旧。

选择编译模型

和其他多数 Boost 库不一样,Boost.Test 包含了一个需要链接的运行时组件:“Program Execution Monitor”(其他的多数都完全实现为头文件)。这个组件(component)包含了 运行测试的主入口点和其他一些东西。如果你是从源码安装的,还需要使用 bjam 来构 建库。静态或动态链接都可以,但需要 configure your includes and build appropriately.

从编写和构建测试的角度来看,最主要的事是在源文件中包含正确的定义并在链接时添加 正确的选项(flags)。我选择动态链接到使用包管理安装的预编译好的库上。这样做需要 做以下两件事:

  1. To define BOOST_TEST_DYN_LINK before including the Boost.Test headers in my source file.
  2. The addition of: -lboost_unit_test_framework to my linker flags.

完成以上就可以开始测试了。

第一个测试用例

先随便写一个将两个整型相加的函数,并添加一个测试用例"universeInOrder"来测试它:

1
2
3
4
5
6
7
8
9
10
11
12
13
#define BOOST_TEST_DYN_LINK
#define BOOST_TEST_MODULE Hello
#include <boost/test/unit_test.hpp>

int add(int i, int j)
{

return i + j;
}

BOOST_AUTO_TEST_CASE(universeInOrder)
{
BOOST_CHECK(add(2, 2) == 4);
}

注意除了 BOOST_TEST_DYN_LINK 我还通过 BOOST_TEST_MODULE 给测试模块定义了名 字。测试本身是使用宏 BOOST_AUTO_TEST_CASE 定义的,参数为用例名称1。最终 断言使用了宏 BOOST_CHECK 。编译运行如下:

1
$ g++ -ohello -lboost_unit_test_framework hello.cpp
$ ./hello
Running 1 test case...

*** No errors detected

很简单,测试通过了。如果把 4 改成 5,故意使测试失败的话,会得到:

$ ./hello
Running 1 test case...
hello.cpp(12): error in "universeInOrder": check add(2, 2) == 5 failed

*** 1 failure detected in test suite "Hello"

这里库的好处就开始显现了:漂亮的错误信息,还有行号和相应的表达式。

更多断言

和很多其他的测试库中的断言不同,一个失败的 BOOST_CHECK 不会立即退出测试,而 是记录问题并继续测试。如果想要立即退出,可以使用 BOOST_REQUIRE:

1
2
3
4
BOOST_AUTO_TEST_CASE(universeInOrder)
{
BOOST_REQUIRE(add(2, 2) == 4);
}

只需要打印 warning 而不要使测试失败时,可以使用 BOOST_WARN. 事实上,许多 Boost.Test 断言都有三种变体:CHECK, REQUIRE, WARN.

功能更丰富的断言也有,常用的有:

  • BOOST_CHECK_MESSAGE: 可以指定一条错误信息为第二个参数。可以传递 string,不 过任何支持操作符"<<"的都可以。
  • BOOST_CHECK_EQUAL: 使用"=="检查相等性。比上面例子中普通的 check 好的地方是 断言失败时可以显示出真实值。
  • BOOST_CHECK_THROW: 检查一条表达式会引起特定类型的异常被抛出。

测试套件(Suites)

当测试用例的数量多起来之后,你需要把它们组织到 Suites 里。注意每个模块(使用 BOOST_TEST_MODULE 定义)已经有了一个同名的顶级 suite。为了分类可以根据需要在 模块定义新的 suites。最简单的方法是使用 auto-registration module,将 test case 用新的宏包起来即可:

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
#define BOOST_TEST_DYN_LINK
#define BOOST_TEST_MODULE Suites
#include <boost/test/unit_test.hpp>

int add(int i, int j)
{

return i + j;
}

BOOST_AUTO_TEST_SUITE(Maths)

BOOST_AUTO_TEST_CASE(universeInOrder)
{
BOOST_CHECK(add(2, 2) == 4);
}

BOOST_AUTO_TEST_SUITE_END()

BOOST_AUTO_TEST_SUITE(Physics)

BOOST_AUTO_TEST_CASE(specialTheory)
{
int e = 32;
int m = 2;
int c = 4;

BOOST_CHECK(e == m * c * c);
}

BOOST_AUTO_TEST_SUITE_END()

在一般情况下,你不会看到测试被分类。要在输出中显示 suites,你可以将 log level 调整为 test_suite:

$ ./suites --log_level=test_suite
Running 2 test cases...
Entering test suite "Suites"
Entering test suite "Maths"
Entering test case "universeInOrder"
Leaving test case "universeInOrder"
Leaving test suite "Maths"
Entering test suite "Physics"
Entering test case "specialTheory"
Leaving test case "specialTheory"
Leaving test suite "Physics"
Leaving test suite "Suites"

*** No errors detected

夹具(Fixtures)

要添加通用的设置和收尾代码的话,Boost.Test 也支持 fixtures。这使用了 C++ 的构 造及析构机制。当然你可以自己通过适当地构造和析构函数来实现相关功能,不过这经常 重复,而且不直观。根据我的经验,最好的方式是将测试用 suite 进行组织,这样你可 以为每个 suite 使用 fixture,然后只需要将 BOOST_AUTO_TEST_SUITE 替换为 BOOST_FIXTURE_TEST_SUITE:

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
37
38
#define BOOST_TEST_DYN_LINK
#define BOOST_TEST_MODULE Fixtures
#include <boost/test/unit_test.hpp>

struct Massive
{
int m;

Massive() : m(2)
{
BOOST_TEST_MESSAGE("setup mass");
}

~Massive()
{
BOOST_TEST_MESSAGE("teardown mass");
}
};

BOOST_FIXTURE_TEST_SUITE(Physics, Massive)

BOOST_AUTO_TEST_CASE(specialTheory)
{
int e = 32;
int c = 4;

BOOST_CHECK(e == m * c * c);
}

BOOST_AUTO_TEST_CASE(newton2)
{
int f = 10;
int a = 5;

BOOST_CHECK(f == m * a);
}

BOOST_AUTO_TEST_SUITE_END()

注意 test cases 可以直接访问 fixture 的 public 成员 "m",这背后使用了继承机制, 所以 protected 成员也可以直接访问。如果使用 logging,就可以看到 fixture 在每个 case 都运行了:

$ ./fixtures --log_level=test_suite
Running 2 test cases...
Entering test suite "Fixtures"
Entering test suite "Physics"
Entering test case "specialTheory"
setup mass
teardown mass
Leaving test case "specialTheory"
Entering test case "newton2"
setup mass
teardown mass
Leaving test case "newton2"
Leaving test suite "Physics"
Leaving test suite "Fixtures"

*** No errors detected

结束

我期望这给了你使用 Boost.Test 一个好的开始。我开头也提到文档其实是很多的──而也 因为太多而让人畏惧。测试娱快!

更新

例子的代码现在可以从GitHub上获得。

Footnotes:

1

也可以定义函数容纳你的测试并搬运注册,不过我觉得自动的方法更简单。

Last Updated 2018-05-26 Sat 14:12.
Render by hexo-renderer-org with Emacs 25.2.2 (Org mode 9.1.13)
0%