googletest试用

近期一段时间在推进自己主动化測试和部署系统的工作。之前大量的測试大多手工或半自己主动完毕,在开发过程中占用了不少宝贵的时间,耗费了不少心力。且有时候因为需求推进的紧迫导致开发在測试的过程中不够慎重。疏忽了对于一些边界情况的考虑。最后在系统上线后才发现问题,在一定程度上给某些用户带来了一些困惑,影响了这部分用户的正常使用。为了减轻开发人员的測试工作量,也为了预防測试工作运行的不彻底,打算借着这段空暇期推进下自己主动化測试的内容。

先简介下我们的系统。整个系统的架构是一个C/S的结构。client有安卓和iOS,client与后端通过HTTP协议来协商。后端採用的技术栈主要是Apache+CGI(一种最古老的服务端编程技术)。使用的编程语言是C++。基于C++的測试框架有非常多,终于我选用了googletest(简称gtest)。由于使用简单,上手快。可扩展性比較强。


曾经没做过測试相关的工作。对于測试的认识也比較粗浅。在我作为开发人员有限的视野里。測试是一项非常神圣的工作。从技术层面上来说,測试是非常有技术含量的工作,牛逼的測试除了基本功扎实。也是一个牛逼的开发;从软件开发过程来说。測试有效地保障了系统的稳定性和性能,减少了软件的成本和风险,是一件相当有意义的事情。但在国内非常多公司,特别是创业类型的小公司(比方我如今所处的),測试这件事情相当不受重视。基本上是谁开发谁负责測试,没有专业的測试负责这件事情。那么软件的好坏全然就要看开发的水平和良心。

尽管如今都提倡DevOps,但实际上非常多开发人员都缺乏測试这方面的知识和意识。


回到gtest这个话题,安装过程比較简单。參考README文件能够搞定。

我下载的是gtest1.7版本号,编译完gtest后须要用ar将gtest-all.o纳入静态库中就可以使用。

怎样使用gtest能够參考gtest的文档或者源代码中提供的sample,实际上我就是看了几个sample就開始写代码了,所以sample的代码一定要看。在我实现的自己主动化測试系统中仅仅用到了gtest中两个feature:Assertions和Test Fixtures。实际上已经能非常好地帮助我完毕这个系统了。代码也相当简洁,请求的构造和编码以及响应的解码能够在Test Fixtures的SetUp函数中完毕,真正校验的部分在各个TEST_F測试用例中完毕就好了;为了做到同一个測试能够被多种请求參数复用,请求參数均以文件配置的形式输入,意味着假设要新增一种情况的输入仅仅须要写一个配置文件就能够了。后来发现gtest也提供了类似的feature:值參数化測试(value-parameterized test)。


在使用gtest的时候。实际上最重要的概念还是assertion,由于不管怎样,终于你须要用assertion来校验你的函数、模块或者系统是否符合正确的输入输出规范,才干够真正检验你的測试是否通过。在编写gtest測试程序的时候,你须要理解这样一个概念:一个測试程序能够包括多个測试用例。而一个測试用例能够包括一到多个測试。当同一个測试用例中的多个測试须要共享对象和子程序时。能够使用test fixture类来帮助完毕。

以下来看一个简单的測试用例:


int Factorial(int n); // Returns the factorial of n

// Tests factorial of 0.
TEST(FactorialTest, HandlesZeroInput) {
  EXPECT_EQ(1, Factorial(0));
}

// Tests factorial of positive numbers.
TEST(FactorialTest, HandlesPositiveInput) {
  EXPECT_EQ(1, Factorial(1));
  EXPECT_EQ(2, Factorial(2));
  EXPECT_EQ(6, Factorial(3));
  EXPECT_EQ(40320, Factorial(8));
}

当中,TEST()宏用来定义測试函数,它包括两个參数。第一个參数表示測试用例的名称,第二个參数表示測试的名称。

在上面的样例中,定义了一个測试用例FactorialTest,而该測试用例包括两个測试HandlesZeroInput和HandlesPositiveInput。

測试中的EXPECT_EQ宏正是assertions的一种。它用来检验两个数值是否相等。再来看一个使用Test Fixture的样例:


template <typename E> // E is the element type.
class Queue {
 public:
  Queue();
  void Enqueue(const E& element);
  E* Dequeue(); // Returns NULL if the queue is empty.
  size_t size() const;
  ...
};

class QueueTest : public ::testing::Test {
 protected:
  virtual void SetUp() {
    q1_.Enqueue(1);
    q2_.Enqueue(2);
    q2_.Enqueue(3);
  }

  virtual void TearDown() {}

  Queue<int> q0_;
  Queue<int> q1_;
  Queue<int> q2_;
};

TEST_F(QueueTest, IsEmptyInitially) {
  EXPECT_EQ(0, q0_.size());
}

TEST_F(QueueTest, DequeueWorks) {
  int* n = q0_.Dequeue();
  EXPECT_EQ(NULL, n);

  n = q1_.Dequeue();
  ASSERT_TRUE(n != NULL);
  EXPECT_EQ(1, *n);
  EXPECT_EQ(0, q1_.size());
  delete n;

  n = q2_.Dequeue();
  ASSERT_TRUE(n != NULL);
  EXPECT_EQ(2, *n);
  EXPECT_EQ(1, q2_.size());
  delete n;
}

上面这个样例測试的是一个先进先出队列的測试代码。首先定义了一个Test Fixture类QueueTest继承::testing::Test类。并实现了SetUp()和TearDown()虚函数。接下来定义了两个測试IsEmptyInitially和DequeueWorks,它们隶属于同一个測试用例QueueTest。注意此时定义測试函数的宏并非TEST()而是TEST_F(),且TEST_F()第一个參数(即測试用例名称)要和Test Fixture类名保持一致。

运行測试的逻辑大概是这样:先创建一个Test Fixture类对象,调用SetUp()完毕初始化。运行測试用例下的某个測试。调用TearDown()完毕清理工作。销毁Test Fixture对象。不同的測试使用不同的Test Fixture对象,因此同一个測试用例下的不同測试在运行它的測试之前都是同样的状态。Test Fixture类中定义的共同拥有成员能够在測试中直接訪问,如样例中的q0_、q1_和q2_。



细心的人可能注意到上面的两个样例都没有main函数,是的,我们来看看上面两个样例的main函数:

int main(int argc, char **argv) {
  ::testing::InitGoogleTest(&argc, argv);
  return RUN_ALL_TESTS();
}

先调用::testing::InitGoogleTest()函数来解析命令行參数,gtest提供了一系列命令行參数供使用,有兴趣的能够在编译生成你的測试程序后,运行时加上-h參数,会有相关的说明。

例如以下:


This program contains tests written using Google Test. You can use the
following command line flags to control its behavior:

Test Selection:
  --gtest_list_tests
      List the names of all tests instead of running them. The name of
      TEST(Foo, Bar) is "Foo.Bar".
  --gtest_filter=POSTIVE_PATTERNS[-NEGATIVE_PATTERNS]
      Run only the tests whose name matches one of the positive patterns but
      none of the negative patterns. '?' matches any single character; '*'
      matches any substring; ':' separates two patterns.
  --gtest_also_run_disabled_tests
      Run all disabled tests too.

Test Execution:
  --gtest_repeat=[COUNT]
      Run the tests repeatedly; use a negative count to repeat forever.
  --gtest_shuffle
      Randomize tests' orders on every iteration.
  --gtest_random_seed=[NUMBER]
      Random number seed to use for shuffling test orders (between 1 and
      99999, or 0 to use a seed based on the current time).

Test Output:
  --gtest_color=(yes|no|auto)
      Enable/disable colored output. The default is auto.
  --gtest_print_time=0
      Don't print the elapsed time of each test.
  --gtest_output=xml[:DIRECTORY_PATH/|:FILE_PATH]
      Generate an XML report in the given directory or with the given file
      name. FILE_PATH defaults to test_details.xml.
  --gtest_stream_result_to=HOST:PORT
      Stream test results to the given server.

Assertion Behavior:
  --gtest_death_test_style=(fast|threadsafe)
      Set the default death test style.
  --gtest_break_on_failure
      Turn assertion failures into debugger break-points.
  --gtest_throw_on_failure
      Turn assertion failures into C++ exceptions.
  --gtest_catch_exceptions=0
      Do not report exceptions as test failures. Instead, allow them
      to crash the program or throw a pop-up (on Windows).

Except for --gtest_list_tests, you can alternatively set the corresponding
environment variable of a flag (all letters in upper-case). For example, to
disable colored text output, you can either specify --gtest_color=no or set
the GTEST_COLOR environment variable to no.

For more information, please read the Google Test documentation at
http://code.google.com/p/googletest/. If you find a bug in Google Test
(not one in your own code or tests), please report it to
<googletestframework@googlegroups.com>.

调用RUN_ALL_TESTS()函数运行你所定义的全部測试。假设你没有使用更加高级的feature,main函数这段代码通常是保持不变的,so最好还是把它们放在头文件中:-),很多其它高级特性能够參考这里

參考链接:
玩转gtest系列 (国人所写,写的比較全面,推荐给不爱看英文文档的)
posted @ 2017-05-21 21:15  jzdwajue  阅读(159)  评论(0编辑  收藏  举报