一、 基本概念:

google test 三种测试用例写法:

TEST(test_suite_name, test_name)

第一种是最基本写法:

#include

int add(int a, int b)

{

return a + b;

}

TEST(testAdd, testArrayAdd)

{

int a[] = {1,2,3,4,5};

int b[] = {5,6,7,8,9};

int c[] = {6,8,10,13,14};

for (int i{0}; i < 5; i++)

{

EXPECT_EQ(c[i], add(a[i], b[i])) << "i = " << i;

}

}

TEST_F(test_fixture, test_name),这种写法是在testCase和框架中的::testing::Test基类之间增加一个测试夹具类,对各测试用例进行统一的变量声明,定义,初始化等相关操作。

class TestTranscript : public ::testing::Test

{

public:

void SetUp() override

{

mockArithmeticsConstructor = std::make_unique();

mockArithmetics = std::make_shared();

EXPECT_CALL(*mockArithmeticsConstructor, construct()).WillRepeatedly(Return(mockArithmetics));

transcript = std::make_shared();

}

void TearDown() override

{

mockArithmeticsConstructor.reset();

}

protected:

std::shared_ptr transcript{nullptr};

std::shared_ptr mockArithmetics{nullptr};

};

TEST_F(TestTranscript, testCompute)

{

EXPECT_CALL(*mockArithmetics, plus(Gt(1),Gt(1))).Times(AnyNumber()).WillRepeatedly(Return(98));

EXPECT_CALL(*mockArithmetics, minus(Lt(100),Le(100))).Times(AnyNumber()).WillRepeatedly(Return(98));

EXPECT_EQ(98, transcript->compute(98, 99, 100));

}

TEST_P(test_suite_name, test_name), parameterized 参数化测试,可以用一系列参数去执行同样的测试逻辑。解决同样的函数需要写多分拷贝然后输入不同的参数的问题。

int add(int a, int b)

{

return a + b;

}

struct Params

{

int a;

int b;

int c;

};

class TestAddSuite: public ::testing::TestWithParam

{};

std::vector params{{1,2,3},{2,3,5},{3,4,8}};

INSTANTIATE_TEST_CASE_P(aTest, TestAddSuite, ::testing::ValuesIn(params));

TEST_P(TestAddSuite, testadd)

{

Params params = GetParam();

EXPECT_EQ(params.c, add(params.a, params.b));

}

要执行测试用例还需要定义一个main函数:

#include

int main(int argc, char** argv)

{

testing::InitGoogleTest(&argc, argv);

return RUN_ALL_TESTS();

}

编译时需要加上链接库,例如:

g++ TestTranscript.cpp main.cpp -lgtest -lgmock -lpthread

我们的产品代码中使用TEST_F宏最多,所以本文将逐步解析TEST_F如何创建测试用例类和类对象,以及如何run的全过程。(文中列出主流程,省略了很多细节,更多细节详见gtest源码。)

二、 类图:

三、代码详解:

1. TEST_F 宏展开:

#define TEST_F(test_fixture, test_name)\

GTEST_TEST_(test_fixture, test_name, test_fixture, \

::testing::internal::GetTypeId())

2. GTEST_TEST_ 宏展开:

#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)() {} \

\

private: \

virtual void TestBody(); \

static ::testing::TestInfo* const test_info_ GTEST_ATTRIBUTE_UNUSED_; \

GTEST_DISALLOW_COPY_AND_ASSIGN_(GTEST_TEST_CLASS_NAME_(test_suite_name, \

test_name)); \

}; \

\

::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

test_suite_name, test_name)>); \

void GTEST_TEST_CLASS_NAME_(test_suite_name, test_name)::TestBody()

上面的代码宏定义不易阅读,举个例子说明:

class FooTest: public ::testing::Test

{

public:

void SetUP(){}

void TearDown{}

}

TEST_F(FooTest, Demo)

{

EXPECT_EQ(1, 1);

}

TEST_F宏展开后如下所示:

class FooTest_Demo_Test : public FooTest

{

public:

FooTest_Demo_Test() {}

private:

virtual void TestBody();

static ::testing::TestInfo* const test_info_;

FooTest_Demo_Test(const FooTest_Demo_Test &);

void operator=(const FooTest_Demo_Test &);

};

::testing::TestInfo*

const FooTest_Demo_Test::test_info_ =

::testing::internal::MakeAndRegisterTestInfo(

"FooTest", "Demo", "", "",

(::testing::internal::GetTestTypeId()),

::testing::Test::SetUpTestCase,

::testing::Test::TearDownTestCase,

new ::testing::internal::TestFactoryImpl< FooTest_Demo_Test>);

void FooTest_Demo_Test::TestBody()

{

switch (0)

case 0:

if (const ::testing::AssertionResult

gtest_ar =

(::testing::internal:: EqHelper<(sizeof(::testing::internal::IsNullLiteralHelper(1)) == 1)>::Compare("1", "1", 1, 1)))

;

else

::testing::internal::AssertHelper(

::testing::TPRT_NONFATAL_FAILURE,

".\\gtest_demo.cpp",

9,

gtest_ar.failure_message()

) = ::testing::Message();

}

3. test_info对象的创建和注册:

3.1 TestFactoryImpl 工厂类的实现,(注意:这里只是创建了TestFactoryImpl工厂类对象并保存起来,而在run的时候才会调用其中的CreateTest()函数。)

template

class TestFactoryImpl : public TestFactoryBase {

public:

Test* CreateTest() override { return new TestClass; }

};

3.2 MakeAndRegisterTestInfo 创建TestInfo,并注册到TestSuite中。

TestInfo* MakeAndRegisterTestInfo(

const char* test_suite_name, const char* name, const char* type_param,

const char* value_param, CodeLocation code_location,

TypeId fixture_class_id, SetUpTestSuiteFunc set_up_tc,

TearDownTestSuiteFunc tear_down_tc, TestFactoryBase* factory) {

TestInfo* const test_info =

new TestInfo(test_suite_name, name, type_param, value_param,

code_location, fixture_class_id, factory);

GetUnitTestImpl()->AddTestInfo(set_up_tc, tear_down_tc, test_info);

return test_info;

}

3.3 AddTestInfo函数先找到对应的TestSuite,再把TestInfo注册进去。

void AddTestInfo(internal::SetUpTestSuiteFunc set_up_tc,

internal::TearDownTestSuiteFunc tear_down_tc,

TestInfo* test_info) {

if (original_working_dir_.IsEmpty()) {

original_working_dir_.Set(FilePath::GetCurrentDir());

GTEST_CHECK_(!original_working_dir_.IsEmpty())

<< "Failed to get the current working directory.";

}

GetTestSuite(test_info->test_suite_name(), test_info->type_param(),

set_up_tc, tear_down_tc)

->AddTestInfo(test_info);

}

3.4 在TestSuite的链表中插入test_info

void TestSuite::AddTestInfo(TestInfo* test_info) {

test_info_list_.push_back(test_info);

test_indices_.push_back(static_cast(test_indices_.size()));

}

4. RUN_ALL_TESTS() 剖析

4.1 RUN_ALL_TESTS() 获取::testing::UnitTest自身的单例,调用run()函数把控制权托管给UnitTestImpl类。

inline int RUN_ALL_TESTS() {

return ::testing::UnitTest::GetInstance()->Run();

}

int UnitTest::Run() {

...

return internal::HandleExceptionsInMethodIfSupported(

impl(),

&internal::UnitTestImpl::RunAllTests,

"auxiliary test code (environments or event listeners)") ? 0 : 1;

}

4.2 UnitTestImpl的RunAllTests接受托管,执行其中保存的TestSuite中的Run()

bool UnitTestImpl::RunAllTests() {

...

for (int test_index = 0; test_index < total_test_suite_count(); test_index++) {

GetMutableSuiteCase(test_index)->Run();

...

}

void TestSuite::Run() {

...

for (int i = 0; i < total_test_count(); i++) {

GetMutableTestInfo(i)->Run();

}

...

}

4.3 TestInfo::Run()函数接受TestSuite::run()的调用,执行其中保存TestInfo::run()

void TestInfo::Run() {

...

Test* const test = internal::HandleExceptionsInMethodIfSupported(

factory_, &internal::TestFactoryBase::CreateTest, // 这里才会真正调用CreateTest()创建TestClass对象,也就是我们定义的TEST_F展开后的内容。

"the test fixture's constructor");

if (!Test::HasFatalFailure() && !Test::IsSkipped()) {

test->Run();

...

}

4.4 最终调到测试用例的Test::TestBody

void Test::Run() {

if (!HasSameFixtureClass()) return;

internal::UnitTestImpl* const impl = internal::GetUnitTestImpl();

impl->os_stack_trace_getter()->UponLeavingGTest();

internal::HandleExceptionsInMethodIfSupported(this, &Test::SetUp, "SetUp()");

if (!HasFatalFailure() && !IsSkipped()) {

impl->os_stack_trace_getter()->UponLeavingGTest();

internal::HandleExceptionsInMethodIfSupported(

this, &Test::TestBody, "the test body"); // 这里调用我们写的Test::TestBody测试内容

}

impl->os_stack_trace_getter()->UponLeavingGTest();

internal::HandleExceptionsInMethodIfSupported(

this, &Test::TearDown, "TearDown()");

}