你沒法忍受Google Test的9個特性

用例描述必須遵循嚴格的標識符規則

這種嚴格的規則,雖然給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());
}

Fixture與TEST_F存在隱晦的繼承關係

這也是上例中將RobotCleaner robot聲明爲protected的緣由。這種隱晦的繼承關係,讓剛剛入門使用Google Test的人都大吃一驚。ide

固然,若是你瞭解過Google Test的實現技術(TEST_F展開後將生成Fixture的一個子類,並自動地註冊到框架中),或者已經習慣了他的的設計,這天然不是問題。函數

TEST, TEST_F的設計容易讓人誤解、誤用

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());
}

重寫SetUp/TearDown時,缺少Override的保護

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;
};

不符合OO的習慣

每一個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());

Global級Fixture不能自動發現

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的致命性缺陷的,例如嚴格的標識符命名規則,這種強制的約束幾乎等於殺了他。

相關文章
相關標籤/搜索