我採用的測試代碼如下:
#include <iostream> #include "gtest.h" TEST(SimpleTest, Test1) { EXPECT_TRUE(1); } TEST(SimpleTest, Test2) { EXPECT_TRUE(2); } class FooTest : public ::testing::Test { protected: static void SetUpTestCase() { shared_resource_ = new char[1024]; } static void TearDownTestCase() { delete [] shared_resource_; shared_resource_ = NULL; } virtual void SetUp() { strcpy(shared_resource_, "SetUp : Called: "); } virtual void TearDown() { strcpy(shared_resource_, "TearDown : Called: "); } static char* shared_resource_; }; char* FooTest::shared_resource_ = NULL; TEST_F(FooTest, Test1) { EXPECT_TRUE(1 < 2)<<"Test1 Equal!"; } TEST_F(FooTest, Test2) { EXPECT_TRUE(2 < 3)<<"Test2 Equal!"; } GTEST_API_ int main(int argc, char **argv) { testing::InitGoogleTest(&argc, argv); return RUN_ALL_TESTS(); }
TEST_*,EXPECT_*,RUN_ALL_TESTS等都是Google test framework的內置宏。爲了調試的方便,我們首先進行宏的展開。感謝VS2008內置了宏展開的功能,具體方法如下:
在VS解決方案資源管理器中右擊需要宏展開的文件“xxx.cpp”-->“屬性”-->“配置屬性”-->“C/C++”-->“預處理器”-->“生成預處理文件”-->“帶行號(/P)”或者“不帶行號(/EP /P)”。
右擊“xxx.cpp”文件,選擇編譯。VS2008會在項目文件夾內會一個“xxx.i”文件,對應的宏展開代碼就在此文件中。
展開後的代碼如下:
class SimpleTest_Test1_Test : public ::testing::Test { public: SimpleTest_Test1_Test(){} private: virtual void TestBody(); static ::testing::TestInfo* const test_info_ ; SimpleTest_Test1_Test(SimpleTest_Test1_Test const &); void operator=(SimpleTest_Test1_Test const &); }; ::testing::TestInfo* const SimpleTest_Test1_Test ::test_info_ = ::testing::internal::MakeAndRegisterTestInfo( "SimpleTest", "Test1", 0, 0, (::testing::internal::GetTestTypeId()), ::testing::Test::SetUpTestCase, ::testing::Test::TearDownTestCase, new ::testing::internal::TestFactoryImpl< SimpleTest_Test1_Test>); void SimpleTest_Test1_Test::TestBody() { switch (0) case 0: default: if (const ::testing::AssertionResult gtest_ar_ = ::testing::AssertionResult(1)) ; else ::testing::internal::AssertHelper( ::testing::TestPartResult::kNonFatalFailure, "c:\\documents and settings\\xu\\桌面\\gtest_all\\gtest_all\\gtest_main.cc", 6, ::testing::internal::GetBoolAssertionFailureMessage( gtest_ar_, "1", "false", "true").c_str()) = ::testing::Message(); } ///////////////////////////////////////////////////////////////////////////////////////////// class SimpleTest_Test2_Test : public ::testing::Test { public: SimpleTest_Test2_Test() {} private: virtual void TestBody(); static ::testing::TestInfo* const test_info_ ; SimpleTest_Test2_Test(SimpleTest_Test2_Test const &); void operator=(SimpleTest_Test2_Test const &); }; ::testing::TestInfo* const SimpleTest_Test2_Test ::test_info_ = ::testing::internal::MakeAndRegisterTestInfo( "SimpleTest", "Test2", 0, 0, (::testing::internal::GetTestTypeId()), ::testing::Test::SetUpTestCase, ::testing::Test::TearDownTestCase, new ::testing::internal::TestFactoryImpl< SimpleTest_Test2_Test>); void SimpleTest_Test2_Test::TestBody() { switch (0) case 0: default: if (const ::testing::AssertionResult gtest_ar_ = ::testing::AssertionResult(2)) ; else ::testing::internal::AssertHelper( ::testing::TestPartResult::kNonFatalFailure, "c:\\documents and settings\\xu\\桌面\\gtest_all\\gtest_all\\gtest_main.cc", 10, ::testing::internal::GetBoolAssertionFailureMessage( gtest_ar_, "2", "false", "true").c_str()) = ::testing::Message(); } ///////////////////////////////////////////////////////////////////////////////////////////// class FooTest : public ::testing::Test { protected: static void SetUpTestCase() { shared_resource_ = new char[1024]; } static void TearDownTestCase() { delete [] shared_resource_; shared_resource_ = 0; } virtual void SetUp() { strcpy(shared_resource_, "SetUp : Called: "); } virtual void TearDown() { strcpy(shared_resource_, "TearDown : Called: "); } static char* shared_resource_; }; char* FooTest::shared_resource_ = 0; ///////////////////////////////////////////////////////////////////////////////////////////// class FooTest_Test1_Test : public FooTest { public: FooTest_Test1_Test() {} private: virtual void TestBody(); static ::testing::TestInfo* const test_info_ ; FooTest_Test1_Test(FooTest_Test1_Test const &); void operator=(FooTest_Test1_Test const &); }; ::testing::TestInfo* const FooTest_Test1_Test ::test_info_ = ::testing::internal::MakeAndRegisterTestInfo( "FooTest", "Test1", 0, 0, (::testing::internal::GetTypeId<FooTest>()), FooTest::SetUpTestCase, FooTest::TearDownTestCase, new ::testing::internal::TestFactoryImpl< FooTest_Test1_Test>); void FooTest_Test1_Test::TestBody() { switch (0) case 0: default: if (const ::testing::AssertionResult gtest_ar_ = ::testing::AssertionResult(1 < 2)) ; else ::testing::internal::AssertHelper( ::testing::TestPartResult::kNonFatalFailure, "c:\\documents and settings\\xu\\桌面\\gtest_all\\gtest_all\\gtest_main.cc", 38, ::testing::internal::GetBoolAssertionFailureMessage( gtest_ar_, "1 < 2", "false", "true").c_str()) = ::testing::Message()<<"Test1 Equal!"; } ///////////////////////////////////////////////////////////////////////////////////////////// class FooTest_Test2_Test : public FooTest { public: FooTest_Test2_Test() {} private: virtual void TestBody(); static ::testing::TestInfo* const test_info_ ; FooTest_Test2_Test(FooTest_Test2_Test const &); void operator=(FooTest_Test2_Test const &); }; ::testing::TestInfo* const FooTest_Test2_Test ::test_info_ = ::testing::internal::MakeAndRegisterTestInfo( "FooTest", "Test2", 0, 0, (::testing::internal::GetTypeId<FooTest>()), FooTest::SetUpTestCase, FooTest::TearDownTestCase, new ::testing::internal::TestFactoryImpl< FooTest_Test2_Test>); void FooTest_Test2_Test::TestBody() { switch (0) case 0: default: if (const ::testing::AssertionResult gtest_ar_ = ::testing::AssertionResult(2 < 3)) ; else ::testing::internal::AssertHelper( ::testing::TestPartResult::kNonFatalFailure, "c:\\documents and settings\\xu\\桌面\\gtest_all\\gtest_all\\gtest_main.cc", 42, ::testing::internal::GetBoolAssertionFailureMessage( gtest_ar_, "2 < 3", "false", "true").c_str()) = ::testing::Message()<<"Test2 Equal!"; } ///////////////////////////////////////////////////////////////////////////////////////////// int main(int argc, char **argv) { testing::InitGoogleTest(&argc, argv); return (::testing::UnitTest::GetInstance()->Run()); }
在進入主函數之前,首先會調用4次MakeAndRegisterTestInfo函數,函數的源代碼如下:
TestInfo* MakeAndRegisterTestInfo( const char* test_case_name, const char* name, const char* type_param, const char* value_param, TypeId fixture_class_id, SetUpTestCaseFunc set_up_tc, TearDownTestCaseFunc tear_down_tc, TestFactoryBase* factory) { TestInfo* const test_info = new TestInfo(test_case_name, name, type_param, value_param, fixture_class_id, factory); GetUnitTestImpl()->AddTestInfo(set_up_tc, tear_down_tc, test_info); return test_info; }
class UnitTestImpl { … void AddTestInfo(Test::SetUpTestCaseFunc set_up_tc, Test::TearDownTestCaseFunc tear_down_tc,TestInfo* test_info) { … GetTestCase(test_info->test_case_name(), test_info->type_param(), set_up_tc, tear_down_tc)->AddTestInfo(test_info); } TestCase* GetTestCase(const char* test_case_name, const char* type_param, Test::SetUpTestCaseFunc set_up_tc, Test::TearDownTestCaseFunc tear_down_tc) { // Can we find a TestCase with the given name? const std::vector<TestCase*>::const_iterator test_case = std::find_if(test_cases_.begin(), test_cases_.end(), TestCaseNameIs(test_case_name)); // 根據TestCaseName的名稱尋找 if (test_case != test_cases_.end()) return *test_case; // No. Let's create one. TestCase* const new_test_case = new TestCase(test_case_name, type_param, set_up_tc, tear_down_tc); … test_cases_.push_back(new_test_case); test_case_indices_.push_back(static_cast<int>(test_case_indices_.size())); return new_test_case; } }
// Adds a test to this test case. Will delete the test upon // destruction of the TestCase object. void TestCase::AddTestInfo(TestInfo * test_info) { test_info_list_.push_back(test_info); test_indices_.push_back(static_cast<int>(test_indices_.size())); }
在進入主函數之前:UnitTestImpl中的vector<TestCase*> test_cases_;擁有的元素如下所示:
進入主函數後的執行流程爲:
int UnitTest::Run() { … return internal::HandleExceptionsInMethodIfSupported( impl(), &internal::UnitTestImpl::RunAllTests, "auxiliary test code (environments or event listeners)") ? 0 : 1; }
template <class T, typename Result> Result HandleExceptionsInMethodIfSupported( T* object, Result (T::*method)(), const char* location) { … return (object->*method)(); }
bool UnitTestImpl::RunAllTests() { ... TestEventListener* repeater = listeners()->repeater(); repeater->OnTestProgramStart(*parent_); // How many times to repeat the tests? We don't want to repeat them // when we are inside the subprocess of a death test. const int repeat = in_subprocess_for_death_test ? 1 : GTEST_FLAG(repeat); // Repeats forever if the repeat count is negative. const bool forever = repeat < 0; for (int i = 0; forever || i != repeat; i++) { … // Tells the unit test event listeners that the tests are about to start. repeater->OnTestIterationStart(*parent_, i); // Runs each test case if there is at least one test to run. if (has_tests_to_run) { … if (!Test::HasFatalFailure()) { for (int test_index = 0; test_index < total_test_case_count(); test_index++) { GetMutableTestCase(test_index)->Run(); } } … } elapsed_time_ = GetTimeInMillis() - start; // Tells the unit test event listener that the tests have just finished. repeater->OnTestIterationEnd(*parent_, i); … } repeater->OnTestProgramEnd(*parent_); return !failed; } TestCase* GetMutableTestCase(int i) { const int index = GetElementOr(test_case_indices_, i, -1); return index < 0 ? NULL : test_cases_[index]; }
// Runs every test in this TestCase. void TestCase::Run() { if (!should_run_) return; internal::UnitTestImpl* const impl = internal::GetUnitTestImpl(); impl->set_current_test_case(this); TestEventListener* repeater = UnitTest::GetInstance()->listeners().repeater(); repeater->OnTestCaseStart(*this); impl->os_stack_trace_getter()->UponLeavingGTest(); internal::HandleExceptionsInMethodIfSupported( this, &TestCase::RunSetUpTestCase, "SetUpTestCase()"); const internal::TimeInMillis start = internal::GetTimeInMillis(); for (int i = 0; i < total_test_count(); i++) { GetMutableTestInfo(i)->Run(); } elapsed_time_ = internal::GetTimeInMillis() - start; impl->os_stack_trace_getter()->UponLeavingGTest(); internal::HandleExceptionsInMethodIfSupported( this, &TestCase::RunTearDownTestCase, "TearDownTestCase()"); repeater->OnTestCaseEnd(*this); impl->set_current_test_case(NULL); } TestInfo* TestCase::GetMutableTestInfo(int i) { const int index = GetElementOr(test_indices_, i, -1); return index < 0 ? NULL : test_info_list_[index]; }
void TestInfo::Run() { if (!should_run_) return; // Tells UnitTest where to store test result. internal::UnitTestImpl* const impl = internal::GetUnitTestImpl(); impl->set_current_test_info(this); TestEventListener* repeater = UnitTest::GetInstance()->listeners().repeater(); // Notifies the unit test event listeners that a test is about to start. repeater->OnTestStart(*this); … // Creates the test object. Test* const test = internal::HandleExceptionsInMethodIfSupported( factory_, &internal::TestFactoryBase::CreateTest, "the test fixture's constructor"); // Runs the test only if the test object was created and its // constructor didn't generate a fatal failure. if ((test != NULL) && !Test::HasFatalFailure()) { test->Run(); } … // Notifies the unit test event listener that a test has just finished. repeater->OnTestEnd(*this); impl->set_current_test_info(NULL); }
void Test::Run() { if (!HasSameFixtureClass()) return; internal::UnitTestImpl* const impl = internal::GetUnitTestImpl(); impl->os_stack_trace_getter()->UponLeavingGTest(); internal::HandleExceptionsInMethodIfSupported(this, &Test::SetUp, "SetUp()"); // We will run the test only if SetUp() was successful. if (!HasFatalFailure()) { impl->os_stack_trace_getter()->UponLeavingGTest(); internal::HandleExceptionsInMethodIfSupported( this, &Test::TestBody, "the test body"); } // However, we want to clean up as much as possible. Hence we will // always call TearDown(), even if SetUp() or the test body has // failed. impl->os_stack_trace_getter()->UponLeavingGTest(); internal::HandleExceptionsInMethodIfSupported( this, &Test::TearDown, "TearDown()"); }
綜上,執行流程簡圖如下:
UnitTestImpl::RunAllTests(){…} --> void TestCase::Run() {…} --> void TestInfo::Run() {…} -->
void Test::Run() {…} --> void Test::TestBody(){…}