一、 基本概念:
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
std::shared_ptr
};
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
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 } 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()"); }