• googletest简介


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

    好的测试框架

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

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

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

    基本操作

    下载安装

    github下载

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

    或者直接下载压缩包

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

    编译安装

    # 解压后进入目录
    cd googletest
    cmake CMakeLists.txt
    make
    sudo make install
    
    • 1
    • 2
    • 3
    • 4
    • 5

    使用方法

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

    # 当不想写 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
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6

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

    重要文件

    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()));                                                            
     }    
    
    • 1
    • 2
    • 3
    • 4
    for (int test_index = 0; test_index < total_test_case_count();test_index++) {                                                                                                       
                GetMutableTestCase(test_index)->Run(); //逐个运行测试用例,执行后面的TestBody()                                                                                  
              }              
    
    • 1
    • 2
    • 3

    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 );
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41

    举例子

    //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));
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    // 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
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9

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

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

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

    googletest

    主要是

    #define TEST(test_suite_name,test_name)
    
    • 1

    类的测试

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

    //里面的方法实现就略了
    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.
      //
      // 
      //
      // If we write NULL instead of
      //
      //   static_cast(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.
      //
      // 
      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());
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51
    • 52
    • 53
    • 54
    • 55
    • 56
    • 57
    • 58
    • 59
    • 60
    • 61
    • 62
    • 63
    • 64
    • 65
    • 66
    • 67
    • 68
    • 69
    • 70
    • 71
    • 72
    • 73
    • 74
    • 75
    • 76
    • 77
    • 78
    • 79
    • 80
    • 81
    • 82
    • 83
    • 84
    • 85
    • 86
    • 87
    • 88
    • 89
    • 90
    • 91
    • 92
    • 93
    • 94
    • 95
    • 96
    • 97
    • 98
    • 99
    • 100
    • 101
    • 102
    • 103
    • 104
    • 105

    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 {
    };
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13

    举例子

    // 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_);
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51
    • 52
    • 53
    • 54
    • 55
    • 56
    • 57
    • 58
    • 59
    • 60
    • 61
    • 62
    • 63
    • 64
    • 65
    • 66
    • 67
    • 68
    • 69
    • 70
    • 71
    • 72
    • 73
    • 74
    • 75
    • 76
    • 77
    • 78
    • 79
    • 80
    • 81
    • 82
    • 83
    • 84

    类型参数化

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

    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);
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20

    例子

    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));
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51
    • 52
    • 53
    • 54
    • 55
    • 56
    • 57
    • 58
    • 59
    • 60
    • 61
    • 62
    • 63
    • 64
    • 65
    • 66
    • 67
    • 68
    • 69
    • 70
    • 71
    • 72
    • 73

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

    // 首先声明测试类型参数化(_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...)
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18

    例子

    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
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51
    • 52
    • 53
    • 54
    • 55
    • 56
    • 57
    • 58
    • 59
    • 60
    • 61
    • 62
    • 63

    事件

    可以通过 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;  //单元测试结束后调用
    };
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51
    • 52
    • 53
    • 54
    • 55
    • 56
    • 57
    • 58
    • 59
    • 60

    注意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;
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51
    • 52
    • 53
    • 54
    • 55
    • 56
    • 57
    • 58
    • 59
    • 60
    • 61
    • 62
    • 63
    • 64
    • 65
    • 66
    • 67
    • 68
    • 69
    • 70
    • 71
    • 72
    • 73
    • 74
    • 75
    • 76
    • 77
    • 78
    • 79
    • 80
    • 81
    • 82
    • 83
    • 84
    • 85
    • 86
    • 87
    • 88
    • 89
    • 90
    • 91
    • 92
    • 93
    • 94
    • 95
    • 96
    • 97
    • 98
    • 99
    • 100
    • 101
    • 102
    • 103
    • 104
    • 105
    • 106
    • 107
    • 108

    例子

    举个例子,检测内存泄漏。
    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_;
    };
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35

    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());
    };
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13

    第二步,设置期望

    EXPECT_CALL(mock_object, method(matchers))
    .Times(cardinality)
    .WillOnce(action)
    .WillRepeatedly(action);
    
    • 1
    • 2
    • 3
    • 4

    第三步,调用

    mock_object obj;
    t1 = obj.func1();
    t2 = objfunc2();
    
    • 1
    • 2
    • 3

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

    EXPECT_CALL(turtle, Forward(100));
    
    • 1

    cardinality:调用次数

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

    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());
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30

    例子

    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;
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37

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

  • 相关阅读:
    SSM+美食论坛系统 毕业设计-附源码191023
    Vue组件传
    LLM基础模型系列:Prompt-Tuning
    【算法|动态规划No30】leetcode5. 最长回文子串
    12 【其它组合式API】
    Flutter 教程之高效且精美的滚动组件Slivers (教程含源码)
    药品企业中的计算机系统验证管理规程
    docker:CentOS安装 docker和默认安装目录
    InnoDB主键和二级索引树、回表
    【云原生】Docker网络原理及Cgroup硬件资源占用控制
  • 原文地址:https://blog.csdn.net/m0_65931372/article/details/126527421