googletest简介

googletest是由谷歌的测试技术团队开发的测试框架,使用c++实现,具有跨平台等特性。

好的测试框架

引用谷歌给出的文档,好的测试应当具备以下特征:

  • 测试应该是独立的和可重复的。调试一个由于其他测试而成功或失败的测试是一件痛苦的事情。googletest通过在不同的对象上运行测试来隔离测试。当测试失败时,googletest允许您单独运行它以快速调试。
  • 测试应该很好地“组织”,并反映出测试代码的结构。googletest将相关测试分组到可以共享数据和子例程的测试套件中。这种通用模式很容易识别,并使测试易于维护。当人们切换项目并开始在新的代码库上工作时,这种一致性尤其有用。
  • 测试应该是“可移植的”和“可重用的”。谷歌有许多与平台无关的代码;它的测试也应该是平台中立的。googletest可以在不同的操作系统上工作,使用不同的编译器,所以googletest测试可以在多种配置下工作。
  • 当测试失败时,他们应该提供尽可能多的关于问题的“信息”。谷歌测试不会在第一次测试失败时停止。相反,它只停止当前的测试并继续下一个测试。还可以设置报告非致命失败的测试,在此之后当前测试将继续进行。因此,您可以在一个运行-编辑-编译周期中检测和修复多个错误。总结一下,测试失败时尽可能多地输出错误信息。通过断言,一次测试发现或修复多个问题。
  • 测试框架应该将测试编写者从日常琐事中解放出来,让他们专注于测试“内容”。Googletest自动跟踪所有定义的测试,并且不要求用户为了运行它们而枚举它们。也就是说,开发人员只需要关注测试本身,自动跟踪所有定义测试而不要枚举它们。
  • 测试应该是“快速的”。使用googletest,您可以在测试之间重用共享资源,并且只需要为设置/拆除支付一次费用,而无需使测试彼此依赖。一句话,测试要高效。

再给一张图
在这里插入图片描述

基本操作

下载安装

github下载

git clone https://github.com/google/googletest.git

或者直接下载压缩包

wget https://github.com/google/googletest/releases/tag/release-1.11.0

编译安装

# 解压后进入目录
cd googletest
cmake CMakeLists.txt
make
sudo make install

使用方法

测试使用在编译之后,实际执行之前。

# 当不想写 main 函数的时候,可以直接引入 libgtest_main.a
g++ sample.cc -o sample -lgtest -lgtest_main -lpthread
g++ sample.cc -o sample -lgmock -lgmock_main -lpthread

# 如果自己写了main函数,就不用引入 libgtest_main.a
g++ sample.cc -o sample -lgtest -lpthread

编译解决语法问题,测试解决逻辑问题。

重要文件

googletest:

  • gtest.h:googletest用来单元测试的头文件
  • libgtest.a:静态测试的接口都在这个静态库中实现
  • libgtest_main.a:里面提供了一个main函数以及初始化libgtest.a的代码

这里可能会有疑问,这里提前写好的main函数怎么调用到我们现写的测试案例的呢?这是因为测试案例都会通过TEST()宏加入到一个全局vector中,main()函数会调用RUN_ALL_TESTS(),这就可以把所有加入到这个vector中的测试案例都执行了。
贴一下TEST()和RUN_ALL_TESTS()的核心代码

void TestCase::AddTestInfo(TestInfo * test_info) {                                                                            
   test_info_list_.push_back(test_info);                                                                                       
   test_indices_.push_back(static_cast<int>(test_indices_.size()));                                                            
 }    
for (int test_index = 0; test_index < total_test_case_count();test_index++) {                                                                                                       
            GetMutableTestCase(test_index)->Run(); //逐个运行测试用例,执行后面的TestBody()                                                                                  
          }              

googlemock:与googletest类似,注意googlemock依赖googletest

  • gmock/gmock.h:都不解释了
  • libgmock.a
  • libgmock_main.a

基本概念

给一个关系图
在这里插入图片描述
一个项目中通常只有一个单元测试,一个单元测试中包含多个测试套件,一个测试套件中包含多个测试案例,一个测试案例中有多个断言。

断言

断言其实之前接触过:assert()。googletest要比这个功能多一些。
断言成对出现,它们测试相同的东西,但对当前函数有不同的影响。 ASSERT_* 版本在失败时产生致命失败,并中止当前函数。 EXPECT_* 版本生成非致命失败,它不会中止当前函数。通常首选EXPECT_* ,因为它们允许在测试中报告一个以上的失败。但是,如果在有问题的断言失败时继续没有意义,则应该使用 ASSERT_* 。也就是说,使用ASSERT,错误就退出;使用EXPECT,错误了没有影响,继续执行。

EXPECT_TRUE( condition );
ASSERT_TRUE( condition );
EXPECT_FALSE( condition );
ASSERT_FALSE( condition );

//二元比较
//等于
EXPECT_EQ( val1 , val2 );
ASSERT_EQ( val1 , val2 );

//不等于,注意比较空指针的时候,使用EXPECT_NE( ptr , nullptr) 而不是 EXPECT_NE( ptr , NULL) 
EXPECT_NE( val1 , val2 );
ASSERT_NE( val1 , val2 );

//小于
EXPECT_LT( val1 , val2 );
ASSERT_LT( val1 , val2 );

//小于等于
EXPECT_LE( val1 , val2 );
ASSERT_LE( val1 , val2 );

//大于
EXPECT_GT( val1 , val2 );
ASSERT_GT( val1 , val2 );

//大于等于
EXPECT_GE( val1 , val2 );
ASSERT_GE( val1 , val2 );

//谓词断言,能比 EXPECT_TRUE 提供更详细的错误消息
EXPECT_PRED1( pred , val1 );
EXPECT_PRED2( pred , val1 , val2 );
EXPECT_PRED3( pred , val1 , val2 , val3 );
EXPECT_PRED4( pred , val1 , val2 , val3 , val4 );
EXPECT_PRED5( pred , val1 , val2 , val3 , val4 , val5 );
ASSERT_PRED1( pred , val1 );
ASSERT_PRED2( pred , val1 , val2 );
ASSERT_PRED3( pred , val1 , val2 , val3 );
ASSERT_PRED4( pred , val1 , val2 , val3 , val4 );
ASSERT_PRED5( pred , val1 , val2 , val3 , val4 , val5 );

举例子

//TEST()是一个测试案例,第一个参数是测试套件的名字,第二个参数是测试案例的名字
TEST(FactorialTest, Negative) {
  // This test is named "Negative", and belongs to the "FactorialTest" test case
  EXPECT_EQ(1, Factorial(-5));
  EXPECT_EQ(1, Factorial(-1));
  EXPECT_GT(Factorial(-10), 0);
}

TEST(IsPrimeTest, Negative) {
  // This test belongs to the IsPrimeTest test case.

  EXPECT_FALSE(IsPrime(-1));
  EXPECT_FALSE(IsPrime(-2));
  EXPECT_FALSE(IsPrime(INT_MIN));
}
// Returns true if m and n have no common divisors except 1.
bool MutuallyPrime(int m, int n) { ... }
...
const int a = 3;
const int b = 4;
const int c = 10;
...
EXPECT_PRED2(MutuallyPrime, a, b); // Succeeds
EXPECT_PRED2(MutuallyPrime, b, c); // Fails

所有断言宏都支持输出流,也就是当出现错误的时候,我们可以通过流输出更详细的信息;注意编码问题,经流输出的信息会自动转换为 UTF-8

EXPECT_TRUE(my_condition) << "My condition is not true";

这样会在终端打印出来,可以用来输出更详细的测试信息。

googletest

主要是

#define TEST(test_suite_name,test_name)

类的测试

类的测试逻辑要按照默认构造、传参构造、拷贝构造、具体方法的顺序来测试。这样的话,其他人来看代码的话,就算只看测试代码,也能清楚类的具体实现。也就是说,这个测试案例起到了文档的功能。

//里面的方法实现就略了
class MyString {
 private:
  const char* c_string_;
  const MyString& operator=(const MyString& rhs);

 public:
  // Clones a 0-terminated C string, allocating memory using new.
  static const char* CloneCString(const char* a_c_string);

  
  //
  // C'tors

  // The default c'tor constructs a NULL string.
  MyString() : c_string_(nullptr) {}

  // Constructs a MyString by cloning a 0-terminated C string.
  explicit MyString(const char* a_c_string) : c_string_(nullptr) {
    Set(a_c_string);
  }

  // Copy c'tor
  MyString(const MyString& string) : c_string_(nullptr) {
    Set(string.c_string_);
  }

  
  //
  // D'tor.  MyString is intended to be a final class, so the d'tor
  // doesn't need to be virtual.
  ~MyString() { delete[] c_string_; }

  // Gets the 0-terminated C string this MyString object represents.
  const char* c_string() const { return c_string_; }

  size_t Length() const { return c_string_ == nullptr ? 0 : strlen(c_string_); }

  // Sets the 0-terminated C string this MyString object represents.
  void Set(const char* c_string);
};


// Tests the default c'tor.
TEST(MyString, DefaultConstructor) {
  const MyString s;

  // Asserts that s.c_string() returns NULL.
  //
  // <TechnicalDetails>
  //
  // If we write NULL instead of
  //
  //   static_cast<const char *>(NULL)
  //
  // in this assertion, it will generate a warning on gcc 3.4.  The
  // reason is that EXPECT_EQ needs to know the types of its
  // arguments in order to print them when it fails.  Since NULL is
  // #defined as 0, the compiler will use the formatter function for
  // int to print it.  However, gcc thinks that NULL should be used as
  // a pointer, not an int, and therefore complains.
  //
  // The root of the problem is C++'s lack of distinction between the
  // integer number 0 and the null pointer constant.  Unfortunately,
  // we have to live with this fact.
  //
  // </TechnicalDetails>
  EXPECT_STREQ(nullptr, s.c_string());

  EXPECT_EQ(0u, s.Length());
}

const char kHelloString[] = "Hello, world!";

// Tests the c'tor that accepts a C string.
TEST(MyString, ConstructorFromCString) {
  const MyString s(kHelloString);
  EXPECT_EQ(0, strcmp(s.c_string(), kHelloString));
  EXPECT_EQ(sizeof(kHelloString)/sizeof(kHelloString[0]) - 1,
            s.Length());
}

// Tests the copy c'tor.
TEST(MyString, CopyConstructor) {
  const MyString s1(kHelloString);
  const MyString s2 = s1;
  EXPECT_EQ(0, strcmp(s2.c_string(), kHelloString));
}

// Tests the Set method.
TEST(MyString, Set) {
  MyString s;

  s.Set(kHelloString);
  EXPECT_EQ(0, strcmp(s.c_string(), kHelloString));

  // Set should work when the input pointer is the same as the one
  // already in the MyString object.
  s.Set(s.c_string());
  EXPECT_EQ(0, strcmp(s.c_string(), kHelloString));

  // Can we set the MyString to NULL?
  s.Set(nullptr);
  EXPECT_STREQ(nullptr, s.c_string());
}

test fixture测试夹具

在实际测试的过程中,类与类的依赖关系比较紧密,也可能会有多个类来工作。这时就需要包装一个测试对象,这个测试对象就叫测试夹具。
如果希望一些变量在一个测试套件的多个测试案例中都可以使用,就用测试夹具。

// 定义类型,继承自 testing::Test
class TestFixtureSmpl : public testing::Test {
protected:
    void SetUp() {} // 测试夹具测试前调用的函数 -- 做初始化的工作
    void TearDown() {} // 测试夹具测试后调用的函数 -- 做清理的工作
};

// 需要在 TEST_F 中书写测试用例
#define TEST_F(test_fixture,test_name)

// 如果需要复用测试夹具,只需要继承自 TestFixtureSmpl
class TestFixtureSmpl_v2 : public TestFixtureSmpl {
};

举例子

// To use a test fixture, derive a class from testing::Test.
class QueueTestSmpl3 : public testing::Test {
 protected:  // You should make the members protected s.t. they can be
             // accessed from sub-classes.

  // virtual void SetUp() will be called before each test is run.  You
  // should define it if you need to initialize the variables.
  // Otherwise, this can be skipped.
  // override表示编译期间覆盖函数
  void SetUp() override {
    q1_.Enqueue(1);
    q2_.Enqueue(2);
    q2_.Enqueue(3);
  }

  // virtual void TearDown() will be called after each test is run.
  // You should define it if there is cleanup work to do.  Otherwise,
  // you don't have to provide it.
  //
  // virtual void TearDown() {
  // }

  // A helper function that some test uses.
  static int Double(int n) {
    return 2*n;
  }

  // A helper function for testing Queue::Map().
  void MapTester(const Queue<int> * q) {
    // Creates a new queue, where each element is twice as big as the
    // corresponding one in q.
    const Queue<int> * const new_q = q->Map(Double);

    // Verifies that the new queue has the same size as q.
    ASSERT_EQ(q->Size(), new_q->Size());

    // Verifies the relationship between the elements of the two queues.
    for (const QueueNode<int>*n1 = q->Head(), *n2 = new_q->Head();
         n1 != nullptr; n1 = n1->next(), n2 = n2->next()) {
      EXPECT_EQ(2 * n1->element(), n2->element());
    }

    delete new_q;
  }

  // Declares the variables your tests want to use.
  Queue<int> q0_;
  Queue<int> q1_;
  Queue<int> q2_;
};

// When you have a test fixture, you define a test using TEST_F
// instead of TEST.

// Tests the default c'tor.
TEST_F(QueueTestSmpl3, DefaultConstructor) {
  // You can access data in the test fixture here.
  EXPECT_EQ(0u, q0_.Size());
}

// Tests Dequeue().
TEST_F(QueueTestSmpl3, Dequeue) {
  int * n = q0_.Dequeue();
  EXPECT_TRUE(n == nullptr);

  n = q1_.Dequeue();
  ASSERT_TRUE(n != nullptr);
  EXPECT_EQ(1, *n);
  EXPECT_EQ(0u, q1_.Size());
  delete n;

  n = q2_.Dequeue();
  ASSERT_TRUE(n != nullptr);
  EXPECT_EQ(2, *n);
  EXPECT_EQ(1u, q2_.Size());
  delete n;
}

// Tests the Queue::Map() function.
TEST_F(QueueTestSmpl3, Map) {
  MapTester(&q0_);
  MapTester(&q1_);
  MapTester(&q2_);
}

类型参数化

有时候相同的接口,有多个实现,依赖测试夹具。下面是复用测试代码流程

using testing::Test;
using testing::Types;

// 先申明测试夹具
template <class T>
class TestFixtureSmpl : public testing::Test {
protected:
    void SetUp() {} // 测试夹具测试前调用的函数 -- 做初始化的工作
    void TearDown() {} // 测试夹具测试后调用的函数 -- 做清理的工作
};

// 枚举测试类型
typedef Types<Class1, Class2, class3> Implementations;
// #define TYPED_TEST_SUITE(CaseName,Types,__VA_ARGS__...)
// 注意 casename 一定要与测试夹具的名字一致
TYPED_TEST_SUITE(TestFixtureSmpl, Implementations);

// #define TYPED_TEST(CaseName,TestName)
// 开始测试, CaseName 要与 TYPED_TEST_SUITE 一致
TYPED_TEST(TestFixtureSmpl, TestName);

例子

template <class T>
class PrimeTableTest : public testing::Test {
protected:
  // The ctor calls the factory function to create a prime table
  // implemented by T.
  PrimeTableTest() : table_(CreatePrimeTable<T>()) {}

  ~PrimeTableTest() override { delete table_; }

  // Note that we test an implementation via the base interface
  // instead of the actual implementation class.  This is important
  // for keeping the tests close to the real world scenario, where the
  // implementation is invoked via the base interface.  It avoids
  // got-yas where the implementation class has a method that shadows
  // a method with the same name (but slightly different argument
  // types) in the base interface, for example.
  PrimeTable* const table_;
};

using testing::Types;

// Google Test offers two ways for reusing tests for different types.
// The first is called "typed tests".  You should use it if you
// already know *all* the types you are gonna exercise when you write
// the tests.

// To write a typed test case, first use
//
//   TYPED_TEST_SUITE(TestCaseName, TypeList);
//
// to declare it and specify the type parameters.  As with TEST_F,
// TestCaseName must match the test fixture name.

// The list of types we want to test.
typedef Types<OnTheFlyPrimeTable, PreCalculatedPrimeTable> Implementations;

TYPED_TEST_SUITE(PrimeTableTest, Implementations);

// Then use TYPED_TEST(TestCaseName, TestName) to define a typed test,
// similar to TEST_F.
TYPED_TEST(PrimeTableTest, ReturnsFalseForNonPrimes) {
  // Inside the test body, you can refer to the type parameter by
  // TypeParam, and refer to the fixture class by TestFixture.  We
  // don't need them in this example.

  // Since we are in the template world, C++ requires explicitly
  // writing 'this->' when referring to members of the fixture class.
  // This is something you have to learn to live with.
  EXPECT_FALSE(this->table_->IsPrime(-5));
  EXPECT_FALSE(this->table_->IsPrime(0));
  EXPECT_FALSE(this->table_->IsPrime(1));
  EXPECT_FALSE(this->table_->IsPrime(4));
  EXPECT_FALSE(this->table_->IsPrime(6));
  EXPECT_FALSE(this->table_->IsPrime(100));
}

TYPED_TEST(PrimeTableTest, ReturnsTrueForPrimes) {
  EXPECT_TRUE(this->table_->IsPrime(2));
  EXPECT_TRUE(this->table_->IsPrime(3));
  EXPECT_TRUE(this->table_->IsPrime(5));
  EXPECT_TRUE(this->table_->IsPrime(7));
  EXPECT_TRUE(this->table_->IsPrime(11));
  EXPECT_TRUE(this->table_->IsPrime(131));
}

TYPED_TEST(PrimeTableTest, CanGetNextPrime) {
  EXPECT_EQ(2, this->table_->GetNextPrime(0));
  EXPECT_EQ(3, this->table_->GetNextPrime(2));
  EXPECT_EQ(5, this->table_->GetNextPrime(3));
  EXPECT_EQ(7, this->table_->GetNextPrime(5));
  EXPECT_EQ(11, this->table_->GetNextPrime(7));
  EXPECT_EQ(131, this->table_->GetNextPrime(128));
}

有时候你写了某个接口,期望其他人实现它,你可能想写一系列测试,确保其他人的实现满足你的测试

// 首先声明测试类型参数化(_P 是 parameterized or pattern)
// #define TYPED_TEST_SUITE_P(SuiteName)
TYPED_TEST_SUITE_P(TestFixtureSmpl);

// 书写测试, suiteName 与上面一致
// #define TYPED_TEST_P(SuiteName,TestName)
TYPED_TEST_P(TestFixtureSmpl,TestName)

// 枚举所有测试
// #define REGISTER_TYPED_TEST_SUITE_P(SuiteName,__VA_ARGS__...)
REGISTER_TYPED_TEST_SUITE_P(TestFixtureSmpl, TestName1,TestName2,...)

// 上面定义的是抽象测试类型
// 其他人实现功能后,开始测试,假如实现了 OnTheFlyPrimeTable 和PreCalculatedPrimeTable
typedef Types<OnTheFlyPrimeTable, PreCalculatedPrimeTable> PrimeTableImplementations;

// #define INSTANTIATE_TYPED_TEST_SUITE_P(Prefix,SuiteName,Types,__VA_ARGS__...)
INSTANTIATE_TYPED_TEST_SUITE_P(instance_name, testcase, typelist...)

例子

template <class T>
class PrimeTableTest2 : public PrimeTableTest<T> {
};

// Then, declare the test case.  The argument is the name of the test
// fixture, and also the name of the test case (as usual).  The _P
// suffix is for "parameterized" or "pattern".
TYPED_TEST_SUITE_P(PrimeTableTest2);

// Next, use TYPED_TEST_P(TestCaseName, TestName) to define a test,
// similar to what you do with TEST_F.
TYPED_TEST_P(PrimeTableTest2, ReturnsFalseForNonPrimes) {
  EXPECT_FALSE(this->table_->IsPrime(-5));
  EXPECT_FALSE(this->table_->IsPrime(0));
  EXPECT_FALSE(this->table_->IsPrime(1));
  EXPECT_FALSE(this->table_->IsPrime(4));
  EXPECT_FALSE(this->table_->IsPrime(6));
  EXPECT_FALSE(this->table_->IsPrime(100));
}

TYPED_TEST_P(PrimeTableTest2, ReturnsTrueForPrimes) {
  EXPECT_TRUE(this->table_->IsPrime(2));
  EXPECT_TRUE(this->table_->IsPrime(3));
  EXPECT_TRUE(this->table_->IsPrime(5));
  EXPECT_TRUE(this->table_->IsPrime(7));
  EXPECT_TRUE(this->table_->IsPrime(11));
  EXPECT_TRUE(this->table_->IsPrime(131));
}

TYPED_TEST_P(PrimeTableTest2, CanGetNextPrime) {
  EXPECT_EQ(2, this->table_->GetNextPrime(0));
  EXPECT_EQ(3, this->table_->GetNextPrime(2));
  EXPECT_EQ(5, this->table_->GetNextPrime(3));
  EXPECT_EQ(7, this->table_->GetNextPrime(5));
  EXPECT_EQ(11, this->table_->GetNextPrime(7));
  EXPECT_EQ(131, this->table_->GetNextPrime(128));
}

// Type-parameterized tests involve one extra step: you have to
// enumerate the tests you defined:
REGISTER_TYPED_TEST_SUITE_P(
    PrimeTableTest2,  // The first argument is the test case name.
    // The rest of the arguments are the test names.
    ReturnsFalseForNonPrimes, ReturnsTrueForPrimes, CanGetNextPrime);

// At this point the test pattern is done.  However, you don't have
// any real test yet as you haven't said which types you want to run
// the tests with.

// To turn the abstract test pattern into real tests, you instantiate
// it with a list of types.  Usually the test pattern will be defined
// in a .h file, and anyone can #include and instantiate it.  You can
// even instantiate it more than once in the same program.  To tell
// different instances apart, you give each of them a name, which will
// become part of the test case name and can be used in test filters.

// The list of types we want to test.  Note that it doesn't have to be
// defined at the time we write the TYPED_TEST_P()s.
typedef Types<OnTheFlyPrimeTable, PreCalculatedPrimeTable>
    PrimeTableImplementations;
INSTANTIATE_TYPED_TEST_SUITE_P(OnTheFlyAndPreCalculated,    // Instance name
                               PrimeTableTest2,             // Test case name
                               PrimeTableImplementations);  // Type list

事件

可以通过 googletest 的事件机制,在测试前后进行埋点处理

// The interface for tracing execution of tests. The methods are organized in the order the corresponding events are fired.
class TestEventListener {
public:
    virtual ~TestEventListener() {}

    // Fired before any test activity starts.
    virtual void OnTestProgramStart(const UnitTest& unit_test) = 0;  //单元测试前调用

    // Fired before each iteration of tests starts. 
    // There may be more than one iteration if GTEST_FLAG(repeat) is set. 
    // iteration is the iteration index, starting from 0.
    virtual void OnTestIterationStart(const UnitTest& unit_test, int iteration) = 0;

    // Fired before environment set-up for each iteration of tests starts.
    virtual void OnEnvironmentsSetUpStart(const UnitTest& unit_test) = 0;  //设置环境

    // Fired after environment set-up for each iteration of tests ends.
    virtual void OnEnvironmentsSetUpEnd(const UnitTest& unit_test) = 0;

    // Fired before the test suite starts.
    virtual void OnTestSuiteStart(const TestSuite& /*test_suite*/) {}

// Legacy API is deprecated but still available
#ifndef GTEST_REMOVE_LEGACY_TEST_CASEAPI_
    virtual void OnTestCaseStart(const TestCase& /*test_case*/) {}
#endif // GTEST_REMOVE_LEGACY_TEST_CASEAPI_

// Fired before the test starts.
    virtual void OnTestStart(const TestInfo& test_info) = 0;

    // Fired after a failed assertion or a SUCCEED() invocation.
    // If you want to throw an exception from this function to skip to the next
    // TEST, it must be AssertionException defined above, or inherited from it.
    virtual void OnTestPartResult(const TestPartResult& test_part_result) = 0;


    // Fired after the test ends.
    virtual void OnTestEnd(const TestInfo& test_info) = 0;

    // Fired after the test suite ends.
    virtual void OnTestSuiteEnd(const TestSuite& /*test_suite*/) {}

// Legacy API is deprecated but still available
#ifndef GTEST_REMOVE_LEGACY_TEST_CASEAPI_
virtual void OnTestCaseEnd(const TestCase& /*test_case*/) {}

#endif // GTEST_REMOVE_LEGACY_TEST_CASEAPI_

    // Fired before environment tear-down for each iteration of tests starts.
    virtual void OnEnvironmentsTearDownStart(const UnitTest& unit_test) = 0;

    // Fired after environment tear-down for each iteration of tests ends.
    virtual void OnEnvironmentsTearDownEnd(const UnitTest& unit_test) = 0;

    // Fired after each iteration of tests finishes.
    virtual void OnTestIterationEnd(const UnitTest& unit_test, int iteration) = 0;

    // Fired after all test activities have ended.
    virtual void OnTestProgramEnd(const UnitTest& unit_test) = 0;  //单元测试结束后调用
};

注意OnTestProgramStart、OnTestProgramEnd与OnEnvironmentsSetUpStart、OnEnvironmentsSetUpEnd的区别。两者虽然都是全局的,后方是在环境初始化时调用,也就是在main函数中的InitGoogleTest函数中调用。
例子,注意需要自己写main函数

class TersePrinter : public EmptyTestEventListener {
private:
  // Called before any test activity starts.
  void OnTestProgramStart(const UnitTest& /* unit_test */) override {}

  // Called after all test activities have ended.
  void OnTestProgramEnd(const UnitTest& unit_test) override {
    fprintf(stdout, "TEST %s\n", unit_test.Passed() ? "PASSED" : "FAILED");
    fflush(stdout);
  }

  // Called before a test starts.
  void OnTestStart(const TestInfo& test_info) override {
    fprintf(stdout,
            "*** Test %s.%s starting.\n",
            test_info.test_case_name(),
            test_info.name());
    fflush(stdout);
  }

  // Called after a failed assertion or a SUCCEED() invocation.
  void OnTestPartResult(const TestPartResult& test_part_result) override {
    fprintf(stdout,
            "%s in %s:%d\n%s\n",
            test_part_result.failed() ? "*** Failure" : "Success",
            test_part_result.file_name(),
            test_part_result.line_number(),
            test_part_result.summary());
    fflush(stdout);
  }

  // Called after a test ends.
  void OnTestEnd(const TestInfo& test_info) override {
    fprintf(stdout,
            "*** Test %s.%s ending.\n",
            test_info.test_case_name(),
            test_info.name());
    fflush(stdout);
  }
};  // class TersePrinter

TEST(CustomOutputTest, PrintsMessage) {
  printf("Printing something from the test body...\n");
}

TEST(CustomOutputTest, Succeeds) {
  SUCCEED() << "SUCCEED() has been invoked from here";
}

TEST(CustomOutputTest, Fails) {
  EXPECT_EQ(1, 2)
      << "This test fails in order to demonstrate alternative failure messages";
}
}  // namespace

int main(int argc, char **argv) {
  InitGoogleTest(&argc, argv);

  bool terse_output = false;
  if (argc > 1 && strcmp(argv[1], "--terse_output") == 0 )
    terse_output = true;
  else
    printf("%s\n", "Run this program with --terse_output to change the way "
           "it prints its output.");

  UnitTest& unit_test = *UnitTest::GetInstance();

  // If we are given the --terse_output command line flag, suppresses the
  // standard output and attaches own result printer.
  if (terse_output) {
    TestEventListeners& listeners = unit_test.listeners();

    // Removes the default console output listener from the list so it will
    // not receive events from Google Test and won't print any output. Since
    // this operation transfers ownership of the listener to the caller we
    // have to delete it as well.
    delete listeners.Release(listeners.default_result_printer());

    // Adds the custom output listener to the list. It will now receive
    // events from Google Test and print the alternative output. We don't
    // have to worry about deleting it since Google Test assumes ownership
    // over it after adding it to the list.
    listeners.Append(new TersePrinter);
  }
  int ret_val = RUN_ALL_TESTS();

  // This is an example of using the UnitTest reflection API to inspect test
  // results. Here we discount failures from the tests we expected to fail.
  int unexpectedly_failed_tests = 0;
  for (int i = 0; i < unit_test.total_test_suite_count(); ++i) {
    const testing::TestSuite& test_suite = *unit_test.GetTestSuite(i);
    for (int j = 0; j < test_suite.total_test_count(); ++j) {
      const TestInfo& test_info = *test_suite.GetTestInfo(j);
      // Counts failed tests that were not meant to fail (those without
      // 'Fails' in the name).
      if (test_info.result()->Failed() &&
          strcmp(test_info.name(), "Fails") != 0) {
        unexpectedly_failed_tests++;
      }
    }
  }

  // Test that were meant to fail should not affect the test program outcome.
  if (unexpectedly_failed_tests == 0)
    ret_val = 0;

  return ret_val;
}

例子

举个例子,检测内存泄漏。
new 是c++的关键字,主要做两步:

  1. 调用 operator new 分配内存
  2. 调用构造函数在步骤 1 返回的内存地址生成类对象

我们可以通过重载 new 来修改 1 的功能。
delete 与 new 类似,只是是先调用析构函数,再释放内存,我们也可以重载delete函数。

// 重载操作符 new 和 delete,接着用类的静态成员来统计调用 new 和 delete的次数
class CLeakMem {
public:
...
    void* operator new(size_t allocation_size) {
        allocated_++;
        return malloc(allocation_size);
    }

    void operator delete(void* block, size_t /* allocation_size */) {
        allocated_--;
        free(block);
    }
private:
    static int allocated_;
};

int CLeakMem::allocated_ = 0;

class LeakChecker : public EmptyTestEventListener {
private:

    // Called before a test starts.
    void OnTestStart(const TestInfo& /* test_info */) override {
    initially_allocated_ = Water::allocated();
    }

    // Called after a test ends.
    void OnTestEnd(const TestInfo& /* test_info */) override {
        int difference = Water::allocated() - initially_allocated_;
        // You can generate a failure in any event handler except OnTestPartResult. Just use an appropriate Google Test assertion to do it.
        EXPECT_LE(difference, 0) << "Leaked " << difference << " unit(s) of class!";
    }
    int initially_allocated_;
};

googlemock

当你写一个原型或测试,往往不能完全的依赖真实对象。一个 mock 对象实现与一个真实对象相同的接口,但让你在运行时指定它时,如何使用?它应该做什么?(哪些方法将被调用?什么顺序?多少次?有什么参数?会返回什么?等)
可以模拟检查它自己和调用者之间的交互,mock 用于创建模拟类和使用它们:

  • 使用一些简单的宏描述你想要模拟的接口,他们将扩展到你的 mock 类的实现
  • 创建一些模拟对象,并使用直观的语法指定其期望和行为
  • 练习使用模拟对象的代码。 Google Mock会在出现任何违反期望的情况时立即处理

注意googlemock 依赖 googletest;调用 InitGoogleMock 时会自动调用 InitGoogleTest。

适用场景

  • 测试很慢,依赖于太多的库或使用昂贵的资源
  • 测试脆弱,使用的一些资源是不可靠的(例如网络)
  • 测试代码如何处理失败(例如,文件校验和错误),但不容易造成失败
  • 确保模块以正确的方式与其他模块交互,但是很难观察到交互。因此你希望看到观察行动结束时的副作用
  • 想模拟出复杂的依赖

使用步骤

第一步,编写模拟类

#include "gmock/gmock.h" // Brings in Google Mock.
class MockTurtle : public Turtle {
public:
...
    //只是说明语法,不一定是这样调用
    MOCK_METHOD0(PenUp, void());
    MOCK_METHOD0(PenDown, void());
    MOCK_METHOD1(Forward, void(int distance));
    MOCK_METHOD1(Turn, void(int degrees));
    MOCK_METHOD2(GoTo, void(int x, int y));
    MOCK_CONST_METHOD0(GetX, int());
    MOCK_CONST_METHOD0(GetY, int());
};

第二步,设置期望

EXPECT_CALL(mock_object, method(matchers))
.Times(cardinality)
.WillOnce(action)
.WillRepeatedly(action);

第三步,调用

mock_object obj;
t1 = obj.func1();
t2 = objfunc2();

说明一下里面的参数
matchers:期待参数

EXPECT_CALL(turtle, Forward(100));

cardinality:调用次数

// turtle::Forward 将预期调用1次
EXPECT_CALL(turtle, Forward(100)).Times(1);
// turtle::Forward 将预期调用至少1次
EXPECT_CALL(turtle, Forward(100)).Times(AtLeast(1));

action:满足期望做什么

using ::testing::Return;
...
// GetX 第一次调用返回100,第二次调用返回200,第三次返回300
EXPECT_CALL(turtle, GetX())
    .Times(3)
    .WillOnce(Return(100))
    .WillOnce(Return(200))
    .WillOnce(Return(300));

// GetX 第一次调用返回100,第二次调用返回200,第三次返回0
EXPECT_CALL(turtle, GetX())
    .Times(3)
    .WillOnce(Return(100))
    .WillOnce(Return(200));

// GetX 将会返回4次100; WillRepeatedly 中的表达式只会计算一次
int n = 100;
EXPECT_CALL(turtle, GetX())
    .Times(4)
    .WillRepeatedly(Return(n++));

// #2 将会覆盖 #1;调用第三次将会报错
EXPECT_CALL(turtle, Forward(_)); // #1
EXPECT_CALL(turtle, Forward(10)) // #2
    .Times(2);

// 将严格按照 PenDown, Forward, PenUp 调用顺序检查
EXPECT_CALL(turtle, PenDown());
EXPECT_CALL(turtle, Forward(100));
EXPECT_CALL(turtle, PenUp());

例子

class FooInterface {
public:
    virtual ~FooInterface() {}
    virtual std::string getArbitraryString() = 0;
    virtual int getPosition() = 0;
};

class MockFoo : public FooInterface {
public:
    MOCK_METHOD0(getArbitraryString, std::string());
    MOCK_METHOD0(getPosition, int());
};

#include "stdafx.h"
using namespace seamless;
using namespace std;
using ::testing::Return;

int main(int argc, char** argv) {
    ::testing::InitGoogleMock(&argc, argv);
    int n = 100;
    string value = "Hello World!";
    MockFoo mockFoo;
    EXPECT_CALL(mockFoo, getArbitraryString())
        .Times(1)
        .WillOnce(Return(value));
    string returnValue = mockFoo.getArbitraryString();
    cout << "Returned Value: " << returnValue << endl;
    //在这里Times(2)意思是调用两次,但是下边只调用了一次,所以会报出异常
    EXPECT_CALL(mockFoo, getPosition())
        .Times(2)
        .WillRepeatedly(Return(n++));
    int val = mockFoo.getPosition(); //100
    cout << "Returned Value: " << val << endl;
    //getPosition指定了调用两次,这里只调用了一次,所以运行结果显示出错
    return EXIT_SUCCESS;
}

提示一下,这里虽然介绍了这么多,一般不使用googlemock。因为googlemock是参照python和java的mock组件做的,而python和java的mock组件已经成熟,比如模拟类已经不需要我们手写了,有个工具能自动生成。如果要自己手写,可能需要写很长代码,实现起来比较麻烦。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值