为什么有如此多的C++测试框架 - from Google Testing Blog
Why Are There So Many C++ Testing Frameworks? by Zhanyong Wan (Software Engineer)
?
最近貌似有很多人正在開發他們自己的C++測試框架,如果還沒能完工。Wiki上有一個此類框架的不完全列表。因為大多數面向對象編程語言只有1到2個主要的框架,對C++而言這就顯得有趣了。例如,大多數Java開發者都使用JUnit或TestNG。難道C++程序員都是瘋狂的DIY愛好者嗎?
當我們開發并將Google Test(Google的C++測試框架)開源后,人們開始好奇為什么我們要做這件事。簡單的回答是我們沒有能夠找到一個已有的并且能夠滿足需求的C++測試框架。這并不表明現有的框架設計和實現的很糟糕,事實上我們也從中學到了很多偉大的想法和實現技巧。但是Google有如此多的C++項目需要在不同的平臺上用不同的編譯器編譯,同時還有著無數的編譯選項,我們確實需要有這么一個框架能夠在如此復雜環境下處理不同類型和規模的項目。
和有著著名口號“一次編程,到處運行”的Java不同,C++代碼的編寫在一個更復雜多變的環境下進行。由于語言本身的復雜性以及處理底層任務的需求,不同的C++編譯器甚至不同版本之間的兼容性都存在著許多問題。雖然有著C++標準,但是到現在為止還沒有一個編譯器廠商能很好的全面支持。很多時候因為任務需要,你必須使用一些不可移植的擴展或者平臺相關的功能。以上這些原因使得編寫一個能使用不同編譯器在在不同平臺上工作的復雜系統變得十分困難。
更頭大的事還沒完,大多數C++編譯器允許你關閉一些標準語言特性來獲取更好的性能。不喜歡異常?關了。不喜歡動態轉換Dynamic Cast?把動態類型識別RTTI禁用好了,雖然它能提供動態轉換和運行時類型信息訪問。如果你選擇這么做,那么使用到以上特性的代碼就會編譯失敗。許多測試框架都依賴異常,想想你把異常關掉會之后發生什么?但對于我們來說這不是一個問題,因為我們的大多數項目都是禁用異常的。也許你會好奇,但是讓我告訴你,Google Test在默認情況下是不需要異常和動態類型識別的。但是當這些特性被打開時,Google Test會試著去利用它們給你提供一些新功能,比如基于異常的斷言exception assertions。
為什么不寫一個可移植的框架?是的,這是Google Test的最高設計目標,而且很多框架的設計者也已經努力過了。但是,自由不是無代價的。跨平臺的C++開發要求大量額外的精力:
所以,我的結論是:為什么我們有如此多C++測試框架的原因是出在C++實現自身,不同環境之間的變化使得編寫可移植的C++代碼是如此困難。張三的框架可能完美的解決了張三的問題,但是對李四卻一點不適用。
另外一個原因我想是C++自身的限制使得我們無法很好的實現一些功能,為了繞過一些限制大家只好各顯神通了。一個顯著的例子就是C++是一個靜態類型語言并且不提供反射Reflection機制。大多數Java測試框架可以使用反射自動找到你編寫的測試用例,這樣你就不用一個個去手工注冊了。如果需要手動注冊的話很有可能你寫了測試用例而忘記去注冊,并且手工維持更新也是很痛苦的。因為C++沒有提供反射機制,我們只能用不同的方法。不幸的是沒有一個完美的解決方案。一些框架要求你手工注冊測試用例,而另一些框架使用腳本分析你的代碼來找到測試用例,還有一些則利用宏來完成自動注冊。我們認為最后一個使用宏的解決方案是最好的,并且對大多數人都有效。目前有好幾種用宏來實現的方法,各有各的利弊,最終結論還是有待商榷的。
讓我們看一些實際代碼來理解Google Test是如何解決測試用例注冊問題的。最簡單的方法是使用TEST宏來添加一個測試用例。
1 TEST(Subject, HasCertainProperty) { 2 // … testing code goes here … 3 }它定義了一個測試用例來檢查某個Subject是否包含一些特定的屬性。宏自動向Google Test注冊這個測試用例,所以當測試程序執行時這個用例會被覆蓋。
這里有個更加實際的用來檢測階乘函數在使用正整數時能正確工作的例子:
1 TEST(FactorialTest, HandlesPositiveInput) { 2 EXPECT_EQ(1, Factorial(1)); 3 EXPECT_EQ(2, Factorial(2)); 4 EXPECT_EQ(6, Factorial(3)); 5 EXPECT_EQ(40320, Factorial(8)); 6 }最后,許多C++測試框架的作者都忽略了擴展性并且滿足于提供一個打包了的解決方案,這導致的后果就是我們留下了很多解決方案,每個都只針對一些特定的問題而不足夠通用。一個萬能的框架必須提供足夠的擴展能力。我們必須明確無論如何所有人的需求是不可能被同時滿足的。我們可以提供一個一攬子解決方案滿足95%的需求,而不是為了一些很少有人用的功能而塞入大量臃腫的代碼。我們可以開放接口來讓用戶自己根據需要實現那些功能。如果我能輕松的擴展已有框架來實現我需要的特定功能,我就不會選擇重起爐灶寫一個新的框架。但是大多數框架的作者都沒有看到可擴展性的重要。我認為正是這種思路導致了今天這種群魔亂舞的局面。在Google Test的實現中,我們努力使得你可以通過自定義斷言生成更有意義的錯誤信息來簡單的擴展你的測試詞匯表。這里有個簡單的例子顯示如何判斷一個值是否在給定的范圍:
1 bool IsInRange(int value, int low, int high) { 2 return low <= value && value <= high; 3 } 4 5 // ... 6 7 EXPECT_TRUE(IsInRange(SomeFunction(), low, high));當你的斷言失敗時,你只知道函數SomeFunction返回的值不在[low, high]的范圍中,但是你不知道返回值和期望范圍是多少,這就使得代碼的調試變得比較麻煩了。
但是你可以提供自己定制的信息來提供斷言失敗時更有意義的描述:
1 EXPECT_TRUE(IsInRange(SomeFunction(), low, high)) 2 << "SomeFunction() = " << SomeFunction() 3 << ", not in range [" 4 << low << ", " << high << "]";如果考慮到SomeFunction可能每次返回不同的值,我們可以把代碼稍作修改:
1 int result = SomeFunction(); 2 EXPECT_TRUE(IsInRange(result, low, high)) 3 << "result (return value of SomeFunction()) = " << result 4 << ", not in range [" << low << ", " << high << "]";上面的辦法看著可行但是太麻煩了,假如你要調用幾百次EXPECT_TRUE來檢測SomeFunction的返回值怎么辦?我們需要把這種模式抽象為一個可重用的結構。
Google Test允許你自定義類似于如下的測試斷言:
1 AssertionResult IsInRange(int value, int low, int high) { 2 if (value < low) 3 return AssertionFailure() 4 << value << " < lower bound " << low; 5 else if (value > high) 6 return AssertionFailure() 7 << value << " > upper bound " << high; 8 else 9 return AssertionSuccess() 10 << value << " is in range [" 11 << low << ", " << high << "]"; 12 }當范圍是[20, 60]而SomeFunction返回13時如下信息將被打印:
Value of: IsInRange(SomeFunction(), low, high)Actual: false (13 < lower bound 20) Expected: true同樣的IsInRange定義還可以被用于EXPECT_FALSE的情況,例如"EXPECT_FALSE(AnothFunction(), 20, 60)",AnotherFunction返回25,如下信息將被打印:
Value of: IsInRange(AnotherFunction(), low, high)Actual: true (25 is in range [20, 60]) Expected: false通過以上方式,你可以為特定的問題領域建立一個斷言庫,從這些清晰的,陳述性的代碼和有意義的錯誤信息中獲益。
于此一脈相承的還有Google Mock(我們的C++ mocking框架)允許你自定義matchers和系統自帶的matchers一起無差別的使用。我們還在Google Test引入了事件監聽API使得用戶可以自行編寫插件。我們希望用戶能夠充分使用這些特性擴展Google Test和Mock以滿足他們的需求,如果能提交一些好的擴展給我們那是最好不過的了。
就像共產主義一定要實現一樣,總有一天,C++測試框架碎片化的問題也會得到解決。
轉載于:https://www.cnblogs.com/panda_lin/p/why_are_there_so_many_cpp_testing_frameworks.html
總結
以上是生活随笔為你收集整理的为什么有如此多的C++测试框架 - from Google Testing Blog的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: Razor传值到js
- 下一篇: C++实现另一个猜数字游戏