日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

歡迎訪問 生活随笔!

生活随笔

當前位置: 首頁 > 人文社科 > 生活经验 >内容正文

生活经验

Google Test(GTest)使用方法和源码解析——Listener技术分析和应用

發布時間:2023/11/27 生活经验 28 豆豆
生活随笔 收集整理的這篇文章主要介紹了 Google Test(GTest)使用方法和源码解析——Listener技术分析和应用 小編覺得挺不錯的,現在分享給大家,幫大家做個參考.

? ? ? ? 在《Google Test(GTest)使用方法和源碼解析——結果統計機制分析》文中,我么分析了GTest如何對測試結果進行統計的。本文我們將解析其結果輸出所使用到的Listener機制。(轉載請指明出于breaksoftware的csdn博客)

解析

? ? ? ?源碼中,我們經常要和UnitTest類打交道。它提供了一個單例方法返回自己的一個對象,然后各處代碼都在調用這個單例的方法。所以說它是GTest框架中非常重要的銜接環。而在其內部,實際工作的卻是一個UnitTestImpl對象

internal::UnitTestImpl* impl_;

? ? ? ? 該指針在UnitTest構造函數中被新建出來

UnitTest::UnitTest() {impl_ = new internal::UnitTestImpl(this);
}

? ? ? ? 之后,我們調用UnitTest單例的方法,很多都是直接調用該對象的方法。其構造函數是這么寫的

UnitTestImpl::UnitTestImpl(UnitTest* parent): parent_(parent),GTEST_DISABLE_MSC_WARNINGS_PUSH_(4355 /* using this in initializer */)default_global_test_part_result_reporter_(this),default_per_thread_test_part_result_reporter_(this),GTEST_DISABLE_MSC_WARNINGS_POP_()global_test_part_result_repoter_(&default_global_test_part_result_reporter_),per_thread_test_part_result_reporter_(&default_per_thread_test_part_result_reporter_),.......catch_exceptions_(false) {listeners()->SetDefaultResultPrinter(new PrettyUnitTestResultPrinter);
}

? ? ? ? 本文要講解的內容將和上面的代碼有很大的關系。

? ? ? ? 在GTest測試框架中,它提出了一個Listener的概念,以供開發者監聽執行過程。GTest框架就是使用Listener機制實現了結果輸出。我們看下listener基類的設計

class TestEventListener {public:virtual ~TestEventListener() {}// Fired before any test activity starts.virtual void OnTestProgramStart(const UnitTest& unit_test) = 0;// Fired before each iteration of tests starts.  There may be more than// one iteration if GTEST_FLAG(repeat) is set. iteration is the iteration// index, starting from 0.virtual void OnTestIterationStart(const UnitTest& unit_test,int iteration) = 0;// Fired before environment set-up for each iteration of tests starts.virtual void OnEnvironmentsSetUpStart(const UnitTest& unit_test) = 0;// Fired after environment set-up for each iteration of tests ends.virtual void OnEnvironmentsSetUpEnd(const UnitTest& unit_test) = 0;// Fired before the test case starts.virtual void OnTestCaseStart(const TestCase& test_case) = 0;// Fired before the test starts.virtual void OnTestStart(const TestInfo& test_info) = 0;// Fired after a failed assertion or a SUCCEED() invocation.virtual void OnTestPartResult(const TestPartResult& test_part_result) = 0;// Fired after the test ends.virtual void OnTestEnd(const TestInfo& test_info) = 0;// Fired after the test case ends.virtual void OnTestCaseEnd(const TestCase& test_case) = 0;// Fired before environment tear-down for each iteration of tests starts.virtual void OnEnvironmentsTearDownStart(const UnitTest& unit_test) = 0;// Fired after environment tear-down for each iteration of tests ends.virtual void OnEnvironmentsTearDownEnd(const UnitTest& unit_test) = 0;// Fired after each iteration of tests finishes.virtual void OnTestIterationEnd(const UnitTest& unit_test,int iteration) = 0;// Fired after all test activities have ended.virtual void OnTestProgramEnd(const UnitTest& unit_test) = 0;
};

? ? ? ? 它暴露了很多接口,每個都對應于執行過程的一個狀態,比如OnTestCaseStart,字面意思就是測試用例執行開始處(要執行自定義邏輯),此處比較適合輸出測試用例的基本信息;再比如OnTestCaseEnd,是測試用例執行結束處(要執行自定義邏輯),此處比較適合輸出測試用例的執行結果。

? ? ? ? 在UnitTestImpl構造函數中有個listeners()函數,其返回了UnitTestImpl類的TestEventListeners成員變量指針。從名字上看可以看出它是一個Listener的集合,因為用戶可以新增自定義的Listener,所以要將其設計為一個集合。但是實際上它只是集合的封裝

class GTEST_API_ TestEventListeners {private:.........// The actual list of listeners.internal::TestEventRepeater* repeater_;// Listener responsible for the standard result output.TestEventListener* default_result_printer_;// Listener responsible for the creation of the XML output file.TestEventListener* default_xml_generator_;
}

? ? ? ? TestEventRepeater才是Listener的集合,同時它也是繼承于TestEventListener接口類。

class TestEventRepeater : public TestEventListener {public:TestEventRepeater() : forwarding_enabled_(true) {}virtual ~TestEventRepeater();void Append(TestEventListener *listener);TestEventListener* Release(TestEventListener* listener);// Controls whether events will be forwarded to listeners_. Set to false// in death test child processes.bool forwarding_enabled() const { return forwarding_enabled_; }void set_forwarding_enabled(bool enable) { forwarding_enabled_ = enable; }virtual void OnTestProgramStart(const UnitTest& unit_test);virtual void OnTestIterationStart(const UnitTest& unit_test, int iteration);virtual void OnEnvironmentsSetUpStart(const UnitTest& unit_test);virtual void OnEnvironmentsSetUpEnd(const UnitTest& unit_test);virtual void OnTestCaseStart(const TestCase& test_case);virtual void OnTestStart(const TestInfo& test_info);virtual void OnTestPartResult(const TestPartResult& result);virtual void OnTestEnd(const TestInfo& test_info);virtual void OnTestCaseEnd(const TestCase& test_case);virtual void OnEnvironmentsTearDownStart(const UnitTest& unit_test);virtual void OnEnvironmentsTearDownEnd(const UnitTest& unit_test);virtual void OnTestIterationEnd(const UnitTest& unit_test, int iteration);virtual void OnTestProgramEnd(const UnitTest& unit_test);private:// Controls whether events will be forwarded to listeners_. Set to false// in death test child processes.bool forwarding_enabled_;// The list of listeners that receive events.std::vector<TestEventListener*> listeners_;GTEST_DISALLOW_COPY_AND_ASSIGN_(TestEventRepeater);
};

? ? ? ? 這個類的設計也非常有意思,它既在成員變量listeners_中保存了一系列用戶自定義的監聽者(TestEventListener*對象指針),又讓自身繼承于TestEventListener,那就是說它自己也是一個Listener。這點比較像大內總管,自己是個太監,手下也是太監。小太監要干的活,大內總管也要能去做(繼承于TestEventListener),只是大內總管不用自己親手去做,而是調度小太監去做。這樣的功能傳遞是通過類似下面代碼的調用去實現的

// Since most methods are very similar, use macros to reduce boilerplate.
// This defines a member that forwards the call to all listeners.
#define GTEST_REPEATER_METHOD_(Name, Type) \
void TestEventRepeater::Name(const Type& parameter) { \if (forwarding_enabled_) { \for (size_t i = 0; i < listeners_.size(); i++) { \listeners_[i]->Name(parameter); \} \} \
}
// This defines a member that forwards the call to all listeners in reverse
// order.
#define GTEST_REVERSE_REPEATER_METHOD_(Name, Type) \
void TestEventRepeater::Name(const Type& parameter) { \if (forwarding_enabled_) { \for (int i = static_cast<int>(listeners_.size()) - 1; i >= 0; i--) { \listeners_[i]->Name(parameter); \} \} \
}

? ? ? ??TestEventRepeater從TestEventListener繼承來的方法便如此定義

GTEST_REPEATER_METHOD_(OnTestProgramStart, UnitTest)
GTEST_REPEATER_METHOD_(OnEnvironmentsSetUpStart, UnitTest)
GTEST_REPEATER_METHOD_(OnTestCaseStart, TestCase)
GTEST_REPEATER_METHOD_(OnTestStart, TestInfo)
GTEST_REPEATER_METHOD_(OnTestPartResult, TestPartResult)
GTEST_REPEATER_METHOD_(OnEnvironmentsTearDownStart, UnitTest)
GTEST_REVERSE_REPEATER_METHOD_(OnEnvironmentsSetUpEnd, UnitTest)
GTEST_REVERSE_REPEATER_METHOD_(OnEnvironmentsTearDownEnd, UnitTest)
GTEST_REVERSE_REPEATER_METHOD_(OnTestEnd, TestInfo)
GTEST_REVERSE_REPEATER_METHOD_(OnTestCaseEnd, TestCase)
GTEST_REVERSE_REPEATER_METHOD_(OnTestProgramEnd, UnitTest)

? ? ? ? 可以見得這個大內總管,對于上面的指令只是做了一個簡單的判斷就拋給下面每個小太監去做了。

? ? ? ? 說個題外話,個人覺得TestEventRepeater中repeater的翻譯,不要叫做“重復者”,叫“中繼者”比較好。
? ? ? ? 我們再回顧下監聽者的傳遞過程

listeners()->SetDefaultResultPrinter(new PrettyUnitTestResultPrinter);
void TestEventListeners::SetDefaultResultPrinter(TestEventListener* listener) {if (default_result_printer_ != listener) {// It is an error to pass this method a listener that is already in the// list.delete Release(default_result_printer_);default_result_printer_ = listener;if (listener != NULL)Append(listener);}
}
void TestEventListeners::Append(TestEventListener* listener) {repeater_->Append(listener);
}

? ? ? ??SetDefaultResultPrinter方法看著是傳遞一個“結果打印者”,但是它實際要接受一個Listener。這個命名雖然很直觀,但是也讓閱讀代碼的人一下子轉不過彎來:Listener和Printer是一回事啊!

class PrettyUnitTestResultPrinter : public TestEventListener {

? ? ? ??PrettyUnitTestResultPrinter各個事件處理函數我就不羅列了,它們就是一些結果輸出。有興趣的同學可以自己查看。

? ? ? ? 然后我們再來看框架中是如何“觸發”這些事件的。

? ? ? ? 首先是UnitTestImpl::RunAllTests()函數,它處理了幾個比較大級別的事件,比如程序啟動和結束

bool UnitTestImpl::RunAllTests() {......TestEventListener* repeater = listeners()->repeater();......repeater->OnTestProgramStart(*parent_);......for (int i = 0; forever || i != repeat; i++) {  ......  repeater->OnTestIterationStart(*parent_, i);  ......  	if (has_tests_to_run) {// Sets up all environments beforehand.repeater->OnEnvironmentsSetUpStart(*parent_);ForEach(environments_, SetUpEnvironment);repeater->OnEnvironmentsSetUpEnd(*parent_);	// Runs the tests only if there was no fatal failure during global// set-up.if (!Test::HasFatalFailure()) {for (int test_index = 0; test_index < total_test_case_count();test_index++) {GetMutableTestCase(test_index)->Run();}}	  // Tears down all environments in reverse order afterwards.repeater->OnEnvironmentsTearDownStart(*parent_);std::for_each(environments_.rbegin(), environments_.rend(),TearDownEnvironment);repeater->OnEnvironmentsTearDownEnd(*parent_);	  }...... repeater->OnTestIterationEnd(*parent_, i);	...... }...... repeater->OnTestProgramEnd(*parent_);...... 
}

? ? ? ? 然后GetMutableTestCase(test_index)->Run();進入每個測試用例的運行,它只處理了OnTestCaseStart和OnTestCaseEnd兩個事件

void TestCase::Run() {...... internal::UnitTestImpl* const impl = internal::GetUnitTestImpl();...... TestEventListener* repeater = UnitTest::GetInstance()->listeners().repeater();...... repeater->OnTestCaseStart(*this);...... const internal::TimeInMillis start = internal::GetTimeInMillis();for (int i = 0; i < total_test_count(); i++) {GetMutableTestInfo(i)->Run();}...... repeater->OnTestCaseEnd(*this);...... 
}

? ? ? ??GetMutableTestInfo(i)->Run();方法進入測試特例運行,它只處理了OnTestStart和OnTestEnd

void TestInfo::Run() {...... // Tells UnitTest where to store test result.internal::UnitTestImpl* const impl = internal::GetUnitTestImpl();...... TestEventListener* repeater = UnitTest::GetInstance()->listeners().repeater();// Notifies the unit test event listeners that a test is about to start.repeater->OnTestStart(*this);...... // Runs the test only if the test object was created and its// constructor didn't generate a fatal failure.if ((test != NULL) && !Test::HasFatalFailure()) {// This doesn't throw as all user code that can throw are wrapped into// exception handling code.test->Run();}...... // Notifies the unit test event listener that a test has just finished.repeater->OnTestEnd(*this);...... 
}

? ? ? ??test->Run();進入了我們自定義的測試實體,其內部通過層層傳導UnitTestImpl的global_test_part_result_repoter_的函數中

void DefaultGlobalTestPartResultReporter::ReportTestPartResult(const TestPartResult& result) {unit_test_->current_test_result()->AddTestPartResult(result);unit_test_->listeners()->repeater()->OnTestPartResult(result);
}

? ? ? ? 如此,我們便將listener的運行機制給講完了。順便我們也分析了GTest默認結果輸出的實現。

應用

? ? ? ? 要使用Listener技術,我們需要實現一個繼承于?testing::TestEventListener 或testing::EmptyTestEventListener的類。如果繼承于testing::TestEventListener,則需要我們實現所有純虛方法;而如果繼承于testing::EmptyTestEventListener,則我們只要關注于部分我們關心的函數實現即可——因為它已經把所有純虛方法實現為空方法了。

class MinimalistPrinter : public ::testing::EmptyTestEventListener {// Called before a test starts.virtual void OnTestStart(const ::testing::TestInfo& test_info) {printf("*** Test %s.%s starting.\n",test_info.test_case_name(), test_info.name());}// Called after a failed assertion or a SUCCEED() invocation.virtual void OnTestPartResult(const ::testing::TestPartResult& test_part_result) {printf("%s in %s:%d\n%s\n",test_part_result.failed() ? "*** Failure" : "Success",test_part_result.file_name(),test_part_result.line_number(),test_part_result.summary());}// Called after a test ends.virtual void OnTestEnd(const ::testing::TestInfo& test_info) {printf("*** Test %s.%s ending.\n",test_info.test_case_name(), test_info.name());}
};

? ? ? ? 然后我們就需要在main函數中將該Listener的對象加入到框架中。此處有個地方需要注意下,由于Listener是個列表,那就意味著一堆Listener將會被執行,其中包括GTest默認的Listener——之前結果輸出的實現者。如果我們只想讓我們自定義的Listener執行,則要先將默認Listener去掉(下面代碼第3行)。

  ::testing::TestEventListeners& listeners =::testing::UnitTest::GetInstance()->listeners();delete listeners.Release(listeners.default_result_printer());listeners.Append(new MinimalistPrinter);

? ? ? ? 這兒有個一個要注意的是:除了OnTestPartResult()之外的函數,都可以使用GTest判斷類宏進行數據判斷。唯獨OnTestPartResult()里不可以,否則會造成OnTestPartResult()被遞歸調用。

總結

以上是生活随笔為你收集整理的Google Test(GTest)使用方法和源码解析——Listener技术分析和应用的全部內容,希望文章能夠幫你解決所遇到的問題。

如果覺得生活随笔網站內容還不錯,歡迎將生活随笔推薦給好友。