什么是单元测试?
复杂的 C/C++ 代码中很可能有 bug,到代码编写完成之后再来测试就像大海捞针。比较谨慎的办法是,在编写各个代码段时,针对特定的区域(例如,一些包含大量计算的 C 函数或声明队列等数据结构的 C++ 类),添加专门的小测试(单元测试),以在编写代码的同时进行测试。按这种方式构建的回归测试套件包含一套单元测试和一个测试驱动程序,这个程序运行测试并报告结果。
为特定的函数或类生成测试
对于文本编辑器这样复杂的代码,外部测试者无法生成针对特定例程的测试 — 测试者不太了解内部代码组织。Boost 的优势就在于白箱测试 :由开发人员编写测试,对类和函数进行语义检查。这个过程极其重要,因为代码以后的维护者可能会破坏原来的逻辑,这时单元测试就会失败。通过使用白箱测试,常常很容易找到出错的地方,不必使用调试器。
请考虑 清单 1 中的简单字符串类。这个类并不健壮,我们使用 Boost 来测试它。
清单 1. 简单的字符串类
#ifndef _MYSTRING
#define _MYSTRING
class mystring {
char* buffer;
int length;
public:
void setbuffer(char* s) { buffer = s; length = strlen(s); }
char& operator[ ] (const int index) { return buffer[index]; }
int size( ) { return length; }
};
#endif
与字符串相关的一些典型的检查,会验证空字符串的长度是否为 0,访问范围超出索引是否导致错误消息或异常,等等。清单 2 给出了一些值得为任何字符串实现创建的测试。要想运行 清单 2 中的源代码,只需用 g++(或任何符合标准的 C++ 编译器)编译它。注意,不需要单独的主函数,代码也不使用任何链接库:作为 Boost 一部分的 unit_test.hpp 头文件中包含所需的所有定义。
清单 2. 字符串类的单元测试
#define BOOST_TEST_MODULE stringtest
#include <boost/test/included/unit_test.hpp>
#include "./str.h"
BOOST_AUTO_TEST_SUITE (stringtest) // name of the test suite is stringtest
BOOST_AUTO_TEST_CASE (test1)
{
mystring s;
BOOST_CHECK(s.size() == 0);
}
BOOST_AUTO_TEST_CASE (test2)
{
mystring s;
s.setbuffer("hello world");
BOOST_REQUIRE_EQUAL ('h', s[0]); // basic test
}
BOOST_AUTO_TEST_SUITE_END( )
BOOST_AUTO_TEST_SUITE 和 BOOST_AUTO_TEST_SUITE_END 宏分别表示测试套件的开头和结尾。各个测试放在这两个宏之间,从这一点来看,这些宏的语义很像 C++ 名称空间。每个单元测试用 BOOST_AUTO_TEST_CASE 宏来定义。清单 3 给出了 清单 2 中代码的输出。
清单 3. 清单 2 中代码的输出
[arpan@tintin] ./a.out
Running 2 test cases...
test.cpp(10): error in "test1": check s.size() == 0 failed
*** 1 failure detected in