你无法忍受 Google Test 的 9 个特性 >> Topics >> Coding Style



你无法忍受 Google Test 的 9 个特性 » Topics » Coding Style

用例描述必须遵循严格的标识符规则

这种严格的规则,虽然给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的人都大吃一惊。

当然,如果你了解过Google Test的实现技术(TEST_F展开后将生成Fixture的一个子类,并自动地注册到框架中),或者已经习惯了他的的设计,这自然不是问题。

TEST, TEST_F的设计容易让人误解、误用

TEST_F的第一个参数是Fixture的名字。如下例如果被误用为TEST,最理想的情况下,则发生编译时错误。如本例所示,编译器将提示robot是一个未定义的变量。最坏的情况下是,错误发生在运行时,可能存在没有调用预期的SetUp/TearDown的风险。

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而使它们联系在一起,这太过于牵强。

此外,需要提取函数时,要么将函数提取到父类的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无需显式地注册,便能被框架自动发现,设计缺乏统一性,一致性。

#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的致命性缺陷的,例如严格的标识符命名规则,这种强制的约束几乎等于杀了他。


Read full article from 你无法忍受 Google Test 的 9 个特性 » Topics » Coding Style


No comments:

Post a Comment

Labels

Algorithm (219) Lucene (130) LeetCode (97) Database (36) Data Structure (33) text mining (28) Solr (27) java (27) Mathematical Algorithm (26) Difficult Algorithm (25) Logic Thinking (23) Puzzles (23) Bit Algorithms (22) Math (21) List (20) Dynamic Programming (19) Linux (19) Tree (18) Machine Learning (15) EPI (11) Queue (11) Smart Algorithm (11) Operating System (9) Java Basic (8) Recursive Algorithm (8) Stack (8) Eclipse (7) Scala (7) Tika (7) J2EE (6) Monitoring (6) Trie (6) Concurrency (5) Geometry Algorithm (5) Greedy Algorithm (5) Mahout (5) MySQL (5) xpost (5) C (4) Interview (4) Vi (4) regular expression (4) to-do (4) C++ (3) Chrome (3) Divide and Conquer (3) Graph Algorithm (3) Permutation (3) Powershell (3) Random (3) Segment Tree (3) UIMA (3) Union-Find (3) Video (3) Virtualization (3) Windows (3) XML (3) Advanced Data Structure (2) Android (2) Bash (2) Classic Algorithm (2) Debugging (2) Design Pattern (2) Google (2) Hadoop (2) Java Collections (2) Markov Chains (2) Probabilities (2) Shell (2) Site (2) Web Development (2) Workplace (2) angularjs (2) .Net (1) Amazon Interview (1) Android Studio (1) Array (1) Boilerpipe (1) Book Notes (1) ChromeOS (1) Chromebook (1) Codility (1) Desgin (1) Design (1) Divide and Conqure (1) GAE (1) Google Interview (1) Great Stuff (1) Hash (1) High Tech Companies (1) Improving (1) LifeTips (1) Maven (1) Network (1) Performance (1) Programming (1) Resources (1) Sampling (1) Sed (1) Smart Thinking (1) Sort (1) Spark (1) Stanford NLP (1) System Design (1) Trove (1) VIP (1) tools (1)

Popular Posts