本文备忘一个主题的分析过程和结论,即,googleTest框架中是如何调用相关的测试宏的?
TEST TEST_F TEST_P 等等
1,googleTest 环境与简单示例
1.1 下载 googletest 并编译
下载:
$ git clone https://github.com/google/googletest.git
$ git checkout release-1.10.0
编译:
$ mkdir build
$ cd build/
$ export CXXFLAGS="-Wno-error=maybe-uninitialized"
$ cmake ..
$ make -j
$ ls lib/
默认为 release,若debug版本则须:
$ cmake .. -DCMAKE_BUILD_TYPE=Debug
成果:
1.2 示例1 验证函数 add
源码
#include <iostream>
#include "gtest/gtest.h"
int add_int_int(int a, int b){
return a+b;
}
TEST(SumFuncTest, twoNumbers){
EXPECT_EQ(add_int_int(3,4),7);
EXPECT_EQ(27, add_int_int(9, 18));
}
GTEST_API_ int main(int argc, char** argv) {
printf("Running main() from %s\n", __FILE__);
testing::InitGoogleTest(&argc, argv);
return RUN_ALL_TESTS();
}
运行:
1.3 示例 2
#include <gtest/gtest.h>
int Foo(int a, int b)
{
if (a == 0 || b == 0)
{
throw "don't do that";
}
int c = a % b;
if (c == 0)
return b;
return Foo(b, c);
}
TEST(FooTest, HandleNoneZeroInput)
{
EXPECT_EQ(2, Foo(4, 10));
EXPECT_EQ(6, Foo(30, 18));
}
g++ foo.cpp -I ../../googletest/googletest/include -L ../../googletest/build_dbg/lib -lgtest -lgtest_main
编译运行:
1.4 示例3
源码:
#include <iostream>
#include "gtest/gtest.h"
// add_util.cc
float add_from_left(float a, float b, float c, float d, float e)
{
float sum = 0.0;
sum += c;
sum += a;
sum += b;
//sum += c;
sum += d;
sum += e;
/*
sum += a;
sum += b;
sum += c;
sum += d;
sum += e;
*/
printf("add_from_left: sum = %f\n", sum);
return sum;
}
float add_from_right(float a, float b, float c, float d, float e)
{
float sum = 0.0;
sum += e;
sum += d;
sum += c;
sum += b;
sum += a;
printf("add_from_right: sum = %f\n", sum);
return sum;
}
int sum(int a, int b){
return a+b;
}
TEST(AddFuncTest, floatVSfloat) {
printf("AddFuncTest: float sum = %f\n", 1.238f + 3.7f + 0.000353265f + 7898.3f + 12.23209f);
printf("AddFuncTest: double sum = %f\n", 12.23209 + 7898.3 + 0.000353265 + 3.7 + 1.238);
EXPECT_EQ(1.238f + 3.7f + 0.000353265f + 7898.3f + 12.23209f, add_from_left(1.238, 3.7, 0.000353265, 7898.3, 12.23209));
EXPECT_EQ(1.238f + 3.7f + 0.000353265f + 7898.3f + 12.23209f, add_from_right(1.238, 3.7, 0.000353265, 7898.3, 12.23209));
//
}
TEST(AddFuncTest, doubleVSfloat) {
printf("AddFuncTest: float sum = %f\n", 1.238f + 3.7f + 0.000353265f + 7898.3f + 12.23209f);
printf("AddFuncTest: double sum = %f\n", 12.23209 + 7898.3 + 0.000353265 + 3.7 + 1.238);
EXPECT_EQ(1.238f + 3.7f + 0.000353265f + 7898.3f + 12.23209f, add_from_left(1.238, 3.7, 0.000353265, 7898.3, 12.23209));
EXPECT_EQ(1.238 + 3.7 + 0.000353265 + 7898.3 + 12.23209, add_from_right(1.238, 3.7, 0.000353265, 7898.3, 12.23209));
//
}
TEST(SumFuncTest, twoNumbers){
EXPECT_EQ(sum(3,4),7);
EXPECT_EQ(27, sum(9, 18));
}
GTEST_API_ int main(int argc, char** argv) {
printf("Running main() from %s\n", __FILE__);
testing::InitGoogleTest(&argc, argv);
return RUN_ALL_TESTS();
}
Makefile
EXE := hello_gtest_ex hello_gtest_add_int_int
all: $(EXE)
%: %.cpp
g++ -O0 -fno-toplevel-reorder $< -o $@ $(INC) $(LD_FLAGS)
INC := -I../googletest/googletest/include/
LD_FLAGS := -L../googletest/build/lib/ -lgtest -lgtest_main
.PHONY: clean
clean:
-rm -rf $(EXE)
2,示例与源码分析
使用最简单的测试示例,聚焦googletest本身的代码逻辑
观察点,main 函数如何调用到 TEST(...){...} 这种结构中的代码
两种方式互相印证:
方式1,通过编译器的预编译指令 g++ -E ... 生成展开代码;
方式2,通过跟踪源代码,来份些TEST等的展开结果
2.1 TEST
示例代码如上:
simple_gtest.cpp
#include "gtest/gtest.h"
int add_int_int(int a, int b){
return a+b;
}
TEST(SumFuncTest, twoNumbers){
EXPECT_EQ(add_int_int(3,4),7);
}
方式1:
g++ -E simple_gtest.cpp -o simple_gtest.i
展开后,simple_gtest.i文件有8W多行,但是其中对我们理解有意义的也就最尾巴上的几行:
int add_int_int(int a, int b){
return a+b;
}
static_assert(sizeof("SumFuncTest") > 1, "test_suite_name must not be empty");
static_assert(sizeof("twoNumbers") > 1, "test_name must not be empty");
class SumFuncTest_twoNumbers_Test : public ::testing::Test {
public:
SumFuncTest_twoNumbers_Test() {}
private:
virtual void TestBody();
static ::testing::TestInfo* const test_info_ __attribute__ ((unused));
SumFuncTest_twoNumbers_Test(SumFuncTest_twoNumbers_Test const &) = delete; void operator=(SumFuncTest_twoNumbers_Test const &) = delete;
};
::testing::TestInfo* const SumFuncTest_twoNumbers_Test::test_info_ =
::testing::internal::MakeAndRegisterTestInfo( "SumFuncTest", "twoNumbers", nullptr, nullptr, ::testing::internal::CodeLocation("simple_gtest.cpp", 8), (::testing::internal::GetTestTypeId()), ::testing::internal::SuiteApiResolver< ::testing::Test>::GetSetUpCaseOrSuite("simple_gtest.cpp", 8), ::testing::internal::SuiteApiResolver< ::testing::Test>::GetTearDownCaseOrSuite("simple_gtest.cpp", 8), new ::testing::internal::TestFactoryImpl<SumFuncTest_twoNumbers_Test>);
void SumFuncTest_twoNumbers_Test::TestBody()
{
switch (0)
case 0:
default:
if (const ::testing::AssertionResult gtest_ar = (::testing::internal::EqHelper::Compare("add_int_int(3,4)", "7", add_int_int(3,4), 7)))
;
else
::testing::internal::AssertHelper(::testing::TestPartResult::kNonFatalFailure, "simple_gtest.cpp", 9, gtest_ar.failure_message()) = ::testing::Message();
}
分析这段代码会发现,
TEST被展开成为了一个 class SumFuncTest_twoNumbers_Test
它有一个成员函数 TestBody(){....}
观察上述代码中最后一个函数体:void SumFuncTest_twoNumbers_Test::TestBody()
其中出现了被测试的函数等。
这说明,这个函数体中的代码才是是被测试内容,而其外围都是框架。
框架部分只需要把这中类的一个实例添加到某个链表中,然后依次迭代执行每个类的 TestBody成员函数,既可以完成测试任务。
本例中的 class 如下:
通过方法2.来验证一下展开的结果:
第一部分,class 宏
关联TEST宏,我们可以找到如下内容:
#define TEST(test_suite_name, test_name) GTEST_TEST(test_suite_name, test_name)
#define GTEST_TEST(test_suite_name, test_name) \
GTEST_TEST_(test_suite_name, test_name, ::testing::Test, \
::testing::internal::GetTestTypeId())
// Expands to the name of the class that implements the given test.
#define GTEST_TEST_CLASS_NAME_(test_suite_name, test_name) \
test_suite_name##_##test_name##_Test
// Helper macro for defining tests.
#define GTEST_TEST_(test_suite_name, test_name, parent_class, parent_id) \
static_assert(sizeof(GTEST_STRINGIFY_(test_suite_name)) > 1, \
"test_suite_name must not be empty"); \
static_assert(sizeof(GTEST_STRINGIFY_(test_name)) > 1, \
"test_name must not be empty"); \
class GTEST_TEST_CLASS_NAME_(test_suite_name, test_name) \
: public parent_class { \
public: \
GTEST_TEST_CLASS_NAME_(test_suite_name, test_name)() = default; \
~GTEST_TEST_CLASS_NAME_(test_suite_name, test_name)() override = default; \
GTEST_TEST_CLASS_NAME_(test_suite_name, test_name) \
(const GTEST_TEST_CLASS_NAME_(test_suite_name, test_name) &) = delete; \
GTEST_TEST_CLASS_NAME_(test_suite_name, test_name) & operator=( \
const GTEST_TEST_CLASS_NAME_(test_suite_name, \
test_name) &) = delete; /* NOLINT */ \
GTEST_TEST_CLASS_NAME_(test_suite_name, test_name) \
(GTEST_TEST_CLASS_NAME_(test_suite_name, test_name) &&) noexcept = delete; \
GTEST_TEST_CLASS_NAME_(test_suite_name, test_name) & operator=( \
GTEST_TEST_CLASS_NAME_(test_suite_name, \
test_name) &&) noexcept = delete; /* NOLINT */ \
\
private: \
void TestBody() override; \
static ::testing::TestInfo* const test_info_ GTEST_ATTRIBUTE_UNUSED_; \
}; \
\
::testing::TestInfo* const GTEST_TEST_CLASS_NAME_(test_suite_name, \
test_name)::test_info_ = \
::testing::internal::MakeAndRegisterTestInfo( \
#test_suite_name, #test_name, nullptr, nullptr, \
::testing::internal::CodeLocation(__FILE__, __LINE__), (parent_id), \
::testing::internal::SuiteApiResolver< \
parent_class>::GetSetUpCaseOrSuite(__FILE__, __LINE__), \
::testing::internal::SuiteApiResolver< \
parent_class>::GetTearDownCaseOrSuite(__FILE__, __LINE__), \
new ::testing::internal::TestFactoryImpl<GTEST_TEST_CLASS_NAME_( \
test_suite_name, test_name)>); \
void GTEST_TEST_CLASS_NAME_(test_suite_name, test_name)::TestBody()
其中的如下两行:
class GTEST_TEST_CLASS_NAME_(test_suite_name, test_name) \
: public parent_class { \
TEST 的宏充分展开后,会根据TEST(X,Y) 括号中的X、Y字串定义一个完整的类,并且包含成员函数:
TestBody()
但是展开的内容中,没有这个函数的函数体。
这个函数体正好就是TEST(X,Y){Z} 中,{Z}的这个部分,即,
TestBody(){Z}
只需要在整个测试系统中,讲上面展开生成的class的一个实例,insert进一个链表中,并依次迭代执行链表的每一对象的成员函数 TestBody(){Z},即可达到测试目的。
第二部分,函数体中的宏
关于 EXPECT_EQ,我们会发现如下定义:
#define EXPECT_EQ(val1, val2) \
EXPECT_PRED_FORMAT2(::testing::internal::EqHelper::Compare, val1, val2)
而其中的 EXPECT_PRED_FORMAT2 又被展开为如下:
// Binary predicate assertion macros.
#define EXPECT_PRED_FORMAT2(pred_format, v1, v2) \
GTEST_PRED_FORMAT2_(pred_format, v1, v2, GTEST_NONFATAL_FAILURE_)
又 GTEST_PRED_FORMAT2_ 被定义为:
#define GTEST_PRED_FORMAT2_(pred_format, v1, v2, on_failure) \
GTEST_ASSERT_(pred_format(#v1, #v2, v1, v2), on_failure)
而且其中的 GTEST_ASSERT_ 被展开为:
#define GTEST_ASSERT_(expression, on_failure) \
GTEST_AMBIGUOUS_ELSE_BLOCKER_ \
if (const ::testing::AssertionResult gtest_ar = (expression)) \
; \
else \
on_failure(gtest_ar.failure_message())
其中 GTEST_AMBIGUOUS_ELSE_BLOCKER_ 展开为:
#define GTEST_AMBIGUOUS_ELSE_BLOCKER_ \
switch (0) \
case 0: \
default: // NOLINT
于是得到函数体为:
总之,只需要调用这个 TestBody() 函数,即可完成测试任务。