leveldb的TEST部分,是单元测试一个很标准的实现
一。对想要测试的目标(这里是BloomFilterPolicy类),建立一个对应的测试类BloomTest
class BloomTest {
private:
const FilterPolicy* policy_;
......
public:
BloomTest() : policy_(NewBloomFilterPolicy(10)) { }
void Add(const Slice& s){......}
bool Matches(const Slice& s) {......}
.......
};
};
Test类最常见的写法,是包含一个测试目标的指针,一些测试相关功能的函数(这里是Add,Match等)
二。然后调用一个宏,宏的{}里通常调用BloomTest的成员函数,输出一些判定结果等。
TEST(BloomTest, EmptyFilter) {
ASSERT_TRUE(! Matches("hello"));
ASSERT_TRUE(! Matches("world"));
}
宏TEST其实是这样定义的
#define TCONCAT(a,b) TCONCAT1(a,b)
#define TCONCAT1(a,b) a##b
#define TEST(base,name) \
class TCONCAT(_Test_,name) : public base { \
public: \
void _Run(); \
static void _RunIt() { \
TCONCAT(_Test_,name) t; \
t._Run(); \
} \
}; \
bool TCONCAT(_Test_ignored_,name) = \
::leveldb::test::RegisterTest(#base, #name, &TCONCAT(_Test_,name)::_RunIt); \
void TCONCAT(_Test_,name)::_Run()
TEST宏做了3件事
1.继承base(即上面定义的BloomTest类)定义一个新类,类名为base_name。注意在继承的类中,将可以调用父类的public,pretected成员。
2. 新增两个公共的函数接口。_RunIt()为static函数,封装例行测试逻辑,给外界提供统一调用接口;Run() 只给了函数定义行,真正的定义,就是TEST之后的{}里,由用户自定义的那些代码。如1所说,TEST里可以直接调用base类里的成员。
3. 执行RegisterTest,#base,#name将变量名转换为字符串,注册类名和例行测试函数_RunIt。
bool TCONCAT(_Test_ignored_,name) = \
::leveldb::test::RegisterTest(#base, #name, &TCONCAT(_Test_,name)::_RunIt);
这个函数的实际定义如下,将base,name,func放入一个全局的vector tests中
bool RegisterTest(const char* base, const char* name, void (*func)()) {
if (tests == NULL) {
tests = new std::vector<Test>;
}
Test t;
t.base = base;
t.name = name;
t.func = func;
tests->push_back(t);
return true;
}
三。main函数的写法:执行所有测试。
int main(int argc, char** argv) {
return leveldb::test::RunAllTests();
}
RunAllTest()的内部实现,就是将RegisterTest()注册过的函数调用一遍。
for (size_t i = 0; i < tests->size(); i++) {
const Test& t = (*tests)[i];
(*t.func)();
.......
}