GoogleTest Primer

入门

术语

googletest使用Test Case对相关测试进行分组,ISTQB使用Test Suite表示对相关测试进行分组。

googletest中的Test对应ISTQB中的Test Case

googletest最近开始用术语Test Case与Test Suite。首选 API 是TestSuite。旧的 TestCase API 正在慢慢被弃用和重构。

基本概念

一个test suite包含一个或多个test。你应该将test分组到能够表示测试代码结构的test suite中。当test suite中的多个test需要共享公共对象和子例程时,您可以将它们放入一个test fixture类中。

一个测试程序可以包含多个test suite

We’ll now explain how to write a test program, starting at the individual assertion level and building up to tests and test suites.

我们现在将解释如何编写测试程序,从单个断言级别开始,然后构建测试和测试套件。

断言

googletest assertions are macros that resemble function calls. You test a class or function by making assertions about its behavior. When an assertion fails, googletest prints the assertion’s source file and line number location, along with a failure message. You may also supply a custom failure message which will be appended to googletest’s message.

The assertions come in pairs that test the same thing but have different effects on the current function. ASSERT_* versions generate fatal failures when they fail, and abort the current function. EXPECT_* versions generate nonfatal failures, which don’t abort the current function. Usually EXPECT_* are preferred, as they allow more than one failure to be reported in a test. However, you should use ASSERT_* if it doesn’t make sense to continue when the assertion in question fails.

Since a failed ASSERT_* returns from the current function immediately, possibly skipping clean-up code that comes after it, it may cause a space leak. Depending on the nature of the leak, it may or may not be worth fixing - so keep this in mind if you get a heap checker error in addition to assertion errors.

To provide a custom failure message, simply stream it into the macro using the << operator or a sequence of such operators. See the following example, using the ASSERT_EQ and EXPECT_EQ macros to verify value equality:

ASSERT_EQ(x.size(), y.size()) << "Vectors x and y are of unequal length";

for (int i = 0; i < x.size(); ++i) {
  EXPECT_EQ(x[i], y[i]) << "Vectors x and y differ at index " << i;
}

Anything that can be streamed to an ostream can be streamed to an assertion macro–in particular, C strings and string objects. If a wide string (wchar_t*, TCHAR* in UNICODE mode on Windows, or std::wstring) is streamed to an assertion, it will be translated to UTF-8 when printed.

GoogleTest provides a collection of assertions for verifying the behavior of your code in various ways. You can check Boolean conditions, compare values based on relational operators, verify string values, floating-point values, and much more. There are even assertions that enable you to verify more complex states by providing custom predicates. For the complete list of assertions provided by GoogleTest, see the Assertions Reference.

Simple Tests

To create a test:

  1. 使用TEST()宏来定义和命名测试函数。这些是没有返回值的普通 C++ 函数。

    Use the TEST() macro to define and name a test function. These are ordinary C++ functions that don’t return a value.

  2. 在此函数中,连同您想要包含的任何有效 C++ 语句,使用各种 googletest 断言来检查值。

    In this function, along with any valid C++ statements you want to include, use the various googletest assertions to check values.

  3. 测试的结果由断言决定;如果测试中的任何断言失败(致命或非致命),或者如果测试崩溃,则整个测试失败。否则,它成功。

    The test’s result is determined by the assertions; if any assertion in the test fails (either fatally or non-fatally), or if the test crashes, the entire test fails. Otherwise, it succeeds.

// The first argument is the name of the test suite, and the second argument is the test’s name within the test suite. 
// TestSuiteName:test suite name,测试套件的名字,可自定义
// TestName:test name,测试的名字,可自定义
TEST(TestSuiteName, TestName) {
  ... test body ...
}

TEST()论点从一般到具体。第一个参数是测试套件的名称,第二个参数是测试套件中的测试名称。两个名称都必须是有效的 C++ 标识符,并且不应包含任何下划线 ( _)。一个测试的全名由它包含的测试套件和它的个人名称组成。来自不同测试套件的测试可以具有相同的单独名称。

TEST() arguments go from general to specific. The first argument is the name of the test suite, and the second argument is the test’s name within the test suite. Both names must be valid C++ identifiers, and they should not contain any underscores (_). A test’s full name consists of its containing test suite and its individual name. Tests from different test suites can have the same individual name.

例如,让我们采用一个简单的整数函数

For example, let’s take a simple integer function:

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

此功能的测试套件可能如下所示:

A test suite for this function might look like:

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

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

googletest 按测试套件对测试结果进行分组,因此逻辑相关的测试应该在同一个测试套件中;换句话说,它们的第一个参数 TEST()应该是相同的。在上面的示例中,我们有两个测试 HandlesZeroInputHandlesPositiveInput,它们属于同一个测试套件FactorialTest

在命名测试套件和测试时,您应该遵循与命名函数和类相同的约定 。

可用性:Linux、Windows、Mac。

googletest groups the test results by test suites, so logically related tests should be in the same test suite; in other words, the first argument to their TEST() should be the same. In the above example, we have two tests, HandlesZeroInput and HandlesPositiveInput, that belong to the same test suite FactorialTest.

When naming your test suites and tests, you should follow the same convention as for naming functions and classes.

Availability: Linux, Windows, Mac.

Test Fixtures: Using the Same Data Configuration for Multiple Tests

如果你发现你需要写多个相似的测试操作,你可以使用test fixture。它允许你为多个不同的测试重用相同的对象配置。

To create a fixture:

  1. 创建一个test fixture类,该类是::testing::Test的派生类。设置test fixture类的主体部分的权限为protected因为我们希望通过子类访问fixture的成员。
  2. 在test fixture类中声明你计划使用的任何对象。
  3. 有必要的话,编写一个默认构造或SetUp函数去为每个测试准备这个对象。注意这里是SetUp不是Setup
  4. 有必要的话,编写一个析构或TearDown()函数去释放你在SetUp()中分配的资源。要了解什么时候该用构造/析构或什么时候该用SetUp()/TearDown(),请查看FAQ
  5. 如果需要的话,为你的测试定义要共享的子程序。

使用fixture时,使用TEST_F()代替TEST(),因为它允许你访问test fixture类中的对象和子例程。

// TestFixtureName:test suite name,不可自定义,其值为定义的test fixture类的名字
// Testname:test name,可自定义
TEST_F(TestFixtureName, TestName) {
  ... test body ...
}

类似于TEST(),第一个参数TestFixtureName表示测试套件名,但是这里的套件名必须是test fixture类的名字。TEST_F()中的_F表示的就是fixture的意思。

Unfortunately, the C++ macro system does not allow us to create a single macro that can handle both types of tests. Using the wrong macro causes a compiler error.不幸的是,C++ 宏系统不允许我们创建可以处理两种类型测试的单个宏。使用错误的宏会导致编译器错误。

在使用TEST_F()之前,你必须首先定义一个test fixture类。否则,你将会收到编译错误virtual outside class declaration

For each test defined with TEST_F(), googletest will create a fresh test fixture at runtime, immediately initialize it via SetUp(), run the test, clean up by calling TearDown(), and then delete the test fixture. Note that different tests in the same test suite have different test fixture objects, and googletest always deletes a test fixture before it creates the next one. googletest does not reuse the same test fixture for multiple tests. Any changes one test makes to the fixture do not affect other tests.对于用 定义的每个测试TEST_F(),googletest 将在运行时创建一个新的测试夹具,立即通过 初始化它SetUp(),运行测试,通过调用清理TearDown(),然后删除测试夹具。请注意,同一测试套件中的不同测试具有不同的测试夹具对象,并且 googletest 总是在创建下一个测试夹具之前删除一个测试夹具。googletest并没有重用多个测试相同的测试夹具。一个测试对夹具所做的任何更改都不会影响其他测试。

As an example, let’s write tests for a FIFO queue class named Queue, which has the following interface:

我们要为一个名为Queue的类写测试,该类具有以下接口:

As an example, let’s write tests for a FIFO queue class named Queue, which has the following interface:

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

首先,顶一个一个test fixture类。通常,我们会将该类命名为FooTest,其中Foo表示你要测试的类。比如,这里我们要测试Queue类,我们可以将test fixture类的名字命名为QueueTest

// 定义一个test fixture类,类名为QueueTest,该类继承自testing::Test
class QueueTest : public ::testing::Test {
 protected:
  void SetUp() override {
     q1_.Enqueue(1);
     q2_.Enqueue(2);
     q2_.Enqueue(3);
  }

  // void TearDown() override {}

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

在这个例子中,我们不需要写TearDown()函数,因为在析构函数运行完之后,没有需要我们清理的东西。

接下来,我们将使用TEST_F()和这个test fixture类实现对Queue的测试。

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

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

  n = q1_.Dequeue();
  ASSERT_NE(n, nullptr);
  EXPECT_EQ(*n, 1);
  EXPECT_EQ(q1_.size(), 0);
  delete n;

  n = q2_.Dequeue();
  ASSERT_NE(n, nullptr);
  EXPECT_EQ(*n, 2);
  EXPECT_EQ(q2_.size(), 1);
  delete n;
}

The above uses both ASSERT_* and EXPECT_* assertions. The rule of thumb is to use EXPECT_* when you want the test to continue to reveal more errors after the assertion failure, and use ASSERT_* when continuing after failure doesn’t make sense. For example, the second assertion in the Dequeue test is ASSERT_NE(n, nullptr), as we need to dereference the pointer n later, which would lead to a segfault when n is NULL.

运行上面的测试代码,会发生下面的情况:

  1. googletest构造一个QueueTest对象(我们暂且称之为t1)。
  2. t1.SetUp()初始化t1
  3. 执行对t1的第一个测试(即IsEmptyInitially)。
  4. 测试结束,调用t1.TearDown()进行清理。(清理什么?)
  5. t1被析构。
  6. 在另一个QueueTest对象上重复上述步骤,这次运行DequeueWorks测试。

Invoking the Tests(调用测试)

TEST()TEST_F()隐式地向googletest注册它们的测试。因此,与许多其他的C++测试框架不同,你不必为了运行它们而重新列出所有定义的测试。

定义完测试之后,你可以通过运行函数RUN_ALL_TEST()执行所有的测试。如果所有的测试均通过,该函数返回0,否则,返回1。要注意的是,RUN_ALL_TEST()会执行你的链接单元中的所有测试,包括来自不同test suite中的测试,甚至包括来自不同源文件中的测试。

When invoked, the RUN_ALL_TESTS() macro:

RUN_ALL_TESTS()被调用是会发生下面的操作:

  • 保存所有的googletest flags的状态
  • 为第一个测试创建一个test fixture对象
  • 通过SetUp()进行初始化
  • 在test fixture对象上执行测试(对test fixture对象进行测试)
  • 通过TearDown()清除test fixture对象数据(清除谁的,如何清除)
  • 删除test fixture对象
  • 恢复所有的googletest flags的状态
  • 对下一个测试重复上述步骤,直到所有测试都运行完毕。

如果其中某一步发生fatal failure,将会跳过后续步骤。

注意:你不能忽略RUN_ALL_TESTS()的返回值,否则你将收到编译错误。这种设计的根本原因是自动化测试服务需要根据其退出代码而不是其stdout/stderr输出来确定测试是否通过;因此你的main()函数必须返回RUN_ALL_TESTS()的值.
RUN_ALL_TESTS()只可以被调用一次。多次调用它会与一些高级 googletest 功能(例如,线程安全 死亡测试)发生冲突, 因此不受支持。

Writing the main() Function(编写main函数)

我们推荐使用链接gtest_main而不是用户自己写main函数,其定义了一个合适的切入点。有关详细信息,请参阅本节末尾。本节其余部分的内容针对的是以下情况:你需要在运行测试之前做一些不能够或者不适合利用test fixture 和 test suites完成的操作。

如果你使用了自己编写的main函数,那么main函数的返回值应该是RUN_ALL_TESTS()的值。

你可以参考下面这个范例:

#include "this/package/foo.h"

#include "gtest/gtest.h"

namespace my {
namespace project {
namespace {

// The fixture for testing class Foo.
class FooTest : public ::testing::Test {
 protected:
  // You can remove any or all of the following functions if their bodies would
  // be empty.

  FooTest() {
     // You can do set-up work for each test here.
  }

  ~FooTest() override {
     // You can do clean-up work that doesn't throw exceptions here.
  }

  // If the constructor and destructor are not enough for setting up
  // and cleaning up each test, you can define the following methods:

  void SetUp() override {
     // Code here will be called immediately after the constructor (right
     // before each test).
  }

  void TearDown() override {
     // Code here will be called immediately after each test (right
     // before the destructor).
  }

  // Class members declared here can be used by all tests in the test suite
  // for Foo.
};

// Tests that the Foo::Bar() method does Abc.
TEST_F(FooTest, MethodBarDoesAbc) {
  const std::string input_filepath = "this/package/testdata/myinputfile.dat";
  const std::string output_filepath = "this/package/testdata/myoutputfile.dat";
  Foo f;
  EXPECT_EQ(f.Bar(input_filepath, output_filepath), 0);
}

// Tests that Foo does Xyz.
TEST_F(FooTest, DoesXyz) {
  // Exercises the Xyz feature of Foo.
}

}  // namespace
}  // namespace project
}  // namespace my

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

::testing::InitGoogleTest()函数会解析命令行中googletest定义的flags,并删除所有可识别的flag。这允许用户通过各种flag来控制测试程序的行为,我们将在 AdvancedGuide中介绍这些flags。必须在调用RUN_ALL_TEST()函数之前调用::testing::InitGoogleTest()函数,否则设定的flags将无法被正确初始化。

在 Windows 上,InitGoogleTest()也适用于宽字符串(wide strings),因此它也可用于以UNICODE模式编译的程序。

你是否觉得编写这些main函数比较费劲?我们真是再同意不过了,这也就是为什么Google Test要提供一个main函数的基本实现的原因。如果你恰好有这方面的需求,那么你只需要将你的测试与gtest_main库链接到一起,就可以了。

NOTE: ParseGUnitFlags() is deprecated in favor of InitGoogleTest().

注意:ParseGUnitFlags()被弃用,取而代之的是InitGoogleTest()

已知限制

  • Google Test 被设计为线程安全的。该实现在pthreads库可用的系统上是线程安全的。目前在其他系统(例如 Windows)上同时使用来自两个线程的 Google Test 断言是 不安全的。在大多数测试中,这不是问题,因为通常断言是在主线程中完成的。如果您想提供帮助,您可以自愿gtest-port.h为您的平台实现必要的同步原语 。
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值