google test 之 TEST_F详解
一、 基本概念:
google test 三种测试用例写法:
- TEST(test_suite_name, test_name)
第一种是最基本写法:
#include <gtest/gtest.h>
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<MockArithmeticsConstructor>();
mockArithmetics = std::make_shared<MockArithmetics>();
EXPECT_CALL(*mockArithmeticsConstructor, construct()).WillRepeatedly(Return(mockArithmetics));
transcript = std::make_shared<Transcript>();
}
void TearDown() override
{
mockArithmeticsConstructor.reset();
}
protected:
std::shared_ptr<Transcript> transcript{nullptr};
std::shared_ptr<MockArithmetics> 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<Params>
{};
std::vector<Params> 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 <gtest/gtest.h>
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<test_fixture>())
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<GTEST_TEST_CLASS_NAME_( \
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 TestClass>
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<int>(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()");
}