這種嚴格的規則,雖然給Google Test的實現帶來了諸多便捷之處,但這給用戶形成了很大的負擔。尤爲當一個用例須要描述一個數學邏輯規則時,用例的表達力將大大折扣。程序員
TEST_F(RobotCleanerTest, at_the_beginning_the_robot_should_be_in_at_the_initial_position) { ASSERT_EQ(Position(0, 0, NORTH), robot.getPosition()); }
以下例,每一個TEST_F
用例都要重複一次RobotCleanerTest
。框架
struct RobotCleanerTest : testing::Test { protected: RobotCleaner robot; }; TEST_F(RobotCleanerTest, at_the_beginning_the_robot_should_be_in_at_the_initial_position) { ASSERT_EQ(Position(0, 0, NORTH), robot.getPosition()); } TEST_F(RobotCleanerTest, should_be_face_west_after_turn_left) { robot.turnLeft(); ASSERT_EQ(Position(0, 0, WEST), robot.getPosition()); }
這也是上例中將RobotCleaner robot
聲明爲protected
的緣由。這種隱晦的繼承關係,讓剛剛入門使用Google Test的人都大吃一驚。ide
固然,若是你瞭解過Google Test的實現技術(TEST_F
展開後將生成Fixture
的一個子類,並自動地註冊到框架中),或者已經習慣了他的的設計,這天然不是問題。函數
TEST_F
的第一個參數是Fixture
的名字。以下例若是被誤用爲TEST
,最理想的狀況下,則發生編譯時錯誤。如本例所示,編譯器將提示robot
是一個未定義的變量。最壞的狀況下是,錯誤發生在運行時,可能存在沒有調用預期的SetUp/TearDown
的風險。spa
struct RobotCleanerTest : testing::Test { protected: RobotCleaner robot; }; TEST_F(RobotCleanerTest, at_the_beginning_the_robot_should_be_in_at_the_initial_position) { ASSERT_EQ(Position(0, 0, NORTH), robot.getPosition()); }
Google Test經過在子類中改寫Setup/TearDown
來定製Fixture
的功能,但程序員每每易於混淆setUp, Setup, SetUp, set_up
,尤爲在C\+\+98中,因爲缺失override
的編譯時保護,易於讓程序員寫出違背原意的邏輯代碼,這樣的錯誤極可能是運行時錯誤。設計
struct RobotCleanerTest : testing::Test { virtual void Setup() // 本應該爲SetUp { robot.reset(); } protected: RobotCleaner robot; };
每一個TEST_F/TEST
的實現,感受是一個個遊離的函數,是一個典型的過程式設計,經過重複地使用RobotCleanerTest
而使它們聯繫在一塊兒,這太過於牽強。code
此外,須要提取函數時,要麼將函數提取到父類的Fixture
中,要麼提取到匿名的namespace
中,物理上隔離很是遠,尤爲用例數目不少的時候,問題更突出。不管怎麼樣,Google Test缺少嚴格意義上的OO設計。繼承
struct RobotCleanerTest : testing::Test { protected: RobotCleaner robot; }; TEST_F(RobotCleanerTest, at_the_beginning_the_robot_should_be_in_at_the_initial_position) { ASSERT_EQ(Position(0, 0, NORTH), robot.getPosition()); } TEST_F(RobotCleanerTest, should_be_face_west_after_turn_left_1_times) { robot.turnLeft(); ASSERT_EQ(Position(0, 0, WEST), robot.getPosition()); } TEST_F(RobotCleanerTest, should_be_face_south_after_turn_left_2_times) { robot.turnLeft(); robot.turnLeft(); ASSERT_EQ(Position(0, 0, SOUTH), robot.getPosition()); } TEST_F(RobotCleanerTest, should_be_face_east_after_turn_left_3_times) { robot.turnLeft(); robot.turnLeft(); robot.turnLeft(); ASSERT_EQ(Position(0, 0, EAST), robot.getPosition()); } TEST_F(RobotCleanerTest, should_be_face_north_after_turn_left_4_times) { robot.turnLeft(); robot.turnLeft(); robot.turnLeft(); robot.turnLeft(); ASSERT_EQ(Position(0, 0, NORTH), robot.getPosition()); }
把指望值放在前面,而把實際值放在後面,嚴重違反了英語的閱讀習慣。猶如我討厭諸如if (NULL != ptr)
的反人類的代碼同樣。部署
ASSERT_EQ(Position(0, 0, WEST), robot.getPosition());
GlobalEnvironment
須要手動註冊到框架,才能被框架發現,而不像TEST_F, TEST, TEST_G
無需顯式地註冊,便能被框架自動發現,設計缺少統一性,一致性。get
#include "gtest/gtest.h" struct GlobalEnvironment : testing::Environment { virtual void SetUp() { ... } virtual void TearDown() { ... } }; int main(int argc, char** argv) { testing::AddGlobalTestEnvironment(new GlobalEnvironment); testing::InitGoogleTest(&argc, argv); return RUN_ALL_TESTS(); }
使用Google Test的斷言時,你能夠在ASSERT_EQ, ASSERT_NE, ASSERT_TRUE, ASSERT_FALSE
之中作選擇。固然你能夠認爲這無可厚非,但這樣的設計最大的問題在於:只能使用框架自己所提供的幾個爲數很少的斷言原語,缺少可擴展性,或者擴展起來很是困難。
例如你想增長個一個ASSERT_NIL
的斷言,擴展起來變得很是不天然。
在C++社區中,有不少人在使用Google Test。這歸功於它在平臺性移植、部署與安裝等方面很是成功,尤爲符合微軟平臺上的C++程序員的胃口;其次,Google Test的TEST, TEST_F
實現的自動發現機制,相對於CppUnit等框架顯得更技高一籌。
但對於高級別的、骨灰級的C++程序員,是沒法容忍上述的Google Test的致命性缺陷的,例如嚴格的標識符命名規則,這種強制的約束幾乎等於殺了他。