时间:2021-07-01 10:21:17 帮助过:26人阅读
RunAllTests() 定义
int RunAllTests() { int num = 0; if (tests != NULL) { for (int i = 0; i < tests->size(); i++) { const Test& t = (*tests)[i]; fprintf(stderr, "==== Test %s.%s\n", t.base, t.name); (*t.func)(); ++num; } } fprintf(stderr, "==== PASSED %d tests\n", num); return 0; }
Test 定义
struct Test { const char* base; const char* name; void (*func)(); };
看其中最简单的一个 TEST 的代码
TEST(DBTest, Empty) { ASSERT_TRUE(db_ != NULL); ASSERT_EQ("NOT_FOUND", Get("foo")); }
看 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() // Register the specified test. Typically not used directly, but // invoked via the macro expansion of TEST. extern bool RegisterTest(const char* base, const char* name, void (*func)());
在宏定义中 # 表示将后面的参数替换成字符串,如:#abc 为 "abc"。 ## 表示粘连符,即将 a 和 b 连成一个字符串,比如:a = abc, b = 123, a##b 为 abc123。#define 为预处理命令,定义了一个标识符及一个串,在源程序中每次遇到该标识符时,均以定义的串代换它。预编译期间进行宏替换:
class _Test_Empty : public DBTest { public: void _Run(); static void _RunIt() { _Test_Empty t; t._Run(); } }; bool _Test_ignored_name = ::leveldb::test::RegisterTest("DBTest", "Empty", &_Test_Empty::_RunIt); // 为全局变量,在 main() 函数运行前执行 void _Test_Empty::_Run() { ASSERT_TRUE(db_ != NULL); ASSERT_EQ("NOT_FOUND", Get("foo")); }
RegisterTest() 定义,这个是注册测试用例的作用,tests 是一个全局 std::vector<Test>指针,存储测试用例。
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; }
以上的代码实现其实非常巧妙,主要目的是用 TEST(xxx, yyy) {} 来定义一段单元测试代码 。思路是这样的,TEST(xxx, yyy)其实定义的是一个宏 ,{} 后面是要测试的代码,其实就是就把他当成一个函数,每声明一个宏就可以定义了一个函数并注册到全局的 tests 中去。这个过程中会定义一个 xxxyyy 的一个专属的测试用例类,而用宏定义实现这个过程主要是为了减少代码的冗余。因为测试用例非常多,如果对每个测试用例都专门编码定义一个类,那代码的冗余是让人无法接受的。其实这种做法也可以借鉴到其他地方来达到减少代码冗余的效果。
leveldb单元测试之宏定义源码剖析
标签:测试用例 func 个人 test extern 处理 c中 call i++