• gtest使用简介


                 gtest使用简介

           gtest是谷歌开发的开源测试框架,用于帮助c++开发者实现测试用例。使用下来感觉gtest简单实用,基本可以满足各类的测试需求。

    gtest的使用并不复杂,这里主要是整理一下基本的使用方法和一些实际开发中碰到的问题。通过 https://github.com/google/googletest 可以得到源码,也有比较详细的使用说明。

    一. 一个简单的例子

    以下例子来自于gtest源码下提供的sample。

    C++
    int Factorial(int n) {
      int result = 1;
      for (int i = 1; i <= n; i++) {
        result *= i;
      }

      return result;
    }

    以上是一个计算阶乘的函数,我们需要写测试代码验证它的正确性。

    C++
    #include "gtest/gtest.h"

    // Tests factorial of negative numbers.
    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);

      //
      //
      // EXPECT_EQ(expected, actual) is the same as
      //
      //   EXPECT_TRUE((expected) == (actual))
      //
      // except that it will print both the expected value and the actual
      // value when the assertion fails.  This is very helpful for
      // debugging.  Therefore in this case EXPECT_EQ is preferred.
      //
      // On the other hand, EXPECT_TRUE accepts any Boolean expression,
      // and is thus more general.
      //
      //

    }

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

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

        如上,测试代码的实现,都通过宏TEST来开始:

    C++
    #define TEST(test_suite_name, test_name) GTEST_TEST(test_suite_name, test_name)

    TEST需要两个参数:第一个是test suite的名字,test suite代表一组相关的测试脚本,比如如上的三个Test的test suite的名字都是FactorialTest,都是针对于函数Factorial的测试;第二参数是为当前测试块的名字。这样做的目的是为了能将测试用例分成不同的层次,当在开发过程中,你只希望针对一些特殊的用例进行测试的时候,可以通过 --gtest_filter 来选择只运行指定的用例。

            TEST展开的结果,简单理解就是它会生成一个名字为test_suite_name##_##test_name##_Test

    的类,该类继承于gtest提供的类Test。比如 TEST(FactorialTest, Zero) 展开的大概样子:

    C++
      class FactorialTest_Zeor_Test
          : public Test {
        ......
        private:                                                                    
          void TestBody() override;                                                  
          static ::testing::TestInfo* const test_info_ GTEST_ATTRIBUTE_UNUSED_;
        }
        
        ::testing::TestInfo* const FactorialTest_Zeor_Test ::test_info_ =   
          ::testing::internal::MakeAndRegisterTestInfo("Factorialtest", "Zeor_Test",
              nullptr, nullptr, ..., new TestFactoryImpl) ;

      void FactorialTest_Zeor_Test::TestBody() {
          EXPECT_EQ(1, Factorial(0));
      }

          TEST展开后,TEST()后跟着的{}中的内容,就是TestBody的实现。这里的TestBody是继承类Test中虚函数的实现。

    再简单看一下test_info_ ,是一个静态变量。在FactorialTest_Zeor_Test中,其实它并没有被用到。它存在的目的是通过MakeAndRegisterTestInfo函数,将test_suite_name,test_name,以及这里定义的FactorialTest_Zeor_Test保存到到一个单例对象中:

    C++
    UnitTest* UnitTest::GetInstance() {
      // CodeGear C++Builder insists on a public destructor for the
      // default implementation.  Use this implementation to keep good OO
      // design with private destructor.

    #if defined(__BORLANDC__)
      static UnitTest* const instance = new UnitTest;
      return instance;
    #else
      static UnitTest instance;
      return &instance;
    #endif  // defined(__BORLANDC__)
    }

           这里的组织形式,

    C++
     在 UnitTestImpl中定义了 test_suites_
     // The vector of TestSuites in their original order.  It owns the
      // elements in the vector.
      std::vector test_suites_;
      
      在 TestSuite 中有一下定义了 test_info_list_
        // The vector of TestInfos in their original order.  It owns the
      // elements in the vector.
      std::vector test_info_list_;

       

        在程序启动阶段,TEST定义的所有信息就会按照以上的形式保存到对应的vecotr中。

        回看一下MakeAndRegisterTestInfo中的最后一个参数 new TestFactoryImpl。这里构建了一个TestFactoryImpl的模板对象:

    C++
    // Defines the abstract factory interface that creates instances
    // of a Test object.
    class TestFactoryBase {
     public:
      virtual ~TestFactoryBase() {}

      // Creates a test instance to run. The instance is both created and destroyed
      // within TestInfoImpl::Run()
      virtual Test* CreateTest() = 0;
      TestFactoryBase() {}

     private:
      TestFactoryBase(const TestFactoryBase&) = delete;
      TestFactoryBase& operator=(const TestFactoryBase&) = delete;
    };
    // This class provides implementation of TestFactoryBase interface.
    // It is used in TEST and TEST_F macros.
    template
    class TestFactoryImpl : public TestFactoryBase {
     public:
      Test* CreateTest() override { return new TestClass; }
    };

    这是个工厂类,目的就是生成以上定义的Test的子类(FactorialTest_Zeor_Test),工厂类对象被保存到了TestInfo中。

    C++
      internal::TestFactoryBase* const factory_;  // The factory that creates
                                                  // the test object

    通过以上的组织方式,不再需要使用者再写其它代码去运行测试用例。而由统一的main函数来完成:

    C++
    GTEST_API_ int main(int argc, char** argv) {
      // Since Google Mock depends on Google Test, InitGoogleMock() is
      // also responsible for initializing Google Test.  Therefore there's
      // no need for calling testing::InitGoogleTest() separately.
      testing::InitGoogleMock(&argc, argv);
      return RUN_ALL_TESTS();
    }

         一般main函数,如上形式。没有特殊需求时候,不需要修改main函数,RUN_ALL_TESTS会根据之前保存到vector中的信息,去运行测试用例。

    二. 运行结果

      编译以上的程序,生成可执行文件sample1_unittest.exe。运行结果如下:

    Bash
    D:\code\googletest-main\out\build\x64-Debug\googletest>sample1_unittest.exe --gtest_filter=FactorialTest.*
    Running main() from D:\code\googletest-main\googletest\src\gtest_main.cc
    Note: Google Test filter = FactorialTest.*
    [==========] Running 3 tests from 1 test suite.
    [----------] Global test environment set-up.
    [----------] 3 tests from FactorialTest
    [ RUN      ] FactorialTest.Negative
    [       OK ] FactorialTest.Negative (0 ms)
    [ RUN      ] FactorialTest.Zero
    [       OK ] FactorialTest.Zero (0 ms)
    [ RUN      ] FactorialTest.Positive
    [       OK ] FactorialTest.Positive (0 ms)
    [----------] 3 tests from FactorialTest (3 ms total)

    [----------] Global test environment tear-down
    [==========] 3 tests from 1 test suite ran. (9 ms total)
    [  PASSED  ] 3 tests.

    可以修改一下,得到一些错误结果。

    Bash
    D:\code\googletest-main\out\build\x64-Debug\googletest>sample1_unittest.exe --gtest_filter=FactorialTest.*
    Running main() from D:\code\googletest-main\googletest\src\gtest_main.cc
    Note: Google Test filter = FactorialTest.*
    [==========] Running 3 tests from 1 test suite.
    [----------] Global test environment set-up.
    [----------] 3 tests from FactorialTest
    [ RUN      ] FactorialTest.Negative
    [       OK ] FactorialTest.Negative (0 ms)
    [ RUN      ] FactorialTest.Zero
    D:\code\googletest-main\googletest\samples\sample1_unittest.cc(100): error: Expected equality of these values:
      10
      Factorial(0)
        Which is: 1
    [  FAILED  ] FactorialTest.Zero (0 ms)
    [ RUN      ] FactorialTest.Positive
    D:\code\googletest-main\googletest\samples\sample1_unittest.cc(107): error: Expected equality of these values:
      40310
      Factorial(8)
        Which is: 40320
    [  FAILED  ] FactorialTest.Positive (0 ms)
    [----------] 3 tests from FactorialTest (1 ms total)

    [----------] Global test environment tear-down
    [==========] 3 tests from 1 test suite ran. (5 ms total)
    [  PASSED  ] 1 test.
    [  FAILED  ] 2 tests, listed below:
    [  FAILED  ] FactorialTest.Zero
    [  FAILED  ] FactorialTest.Positive

     2 FAILED TESTS

    比如将 Factorial(0) 的期望值改为10,如上图测试程序将报出错误信息以及代码所在位置。

           除了在终端显示外,还可以生成文档报告:

    Bash
    --gtest_output=(json|xml)[:DIRECTORY_PATH\|:FILE_PATH]

          通过指定--gtest_output可以生成xml或者json文件形式的报告。

    三. 测试覆盖率(另一个简单的例子)

    在实际的开发过程中,项目中往往要求测试覆盖率,要求尽可能多的覆盖到所有的分支。简单看一下可能要注意到内容:

    C++
    // Returns true if and only if n is a prime number.
    bool IsPrime(int n) {
      // Trivial case 1: small numbers
      if (n <= 1) return false;

      // Trivial case 2: even numbers
      if (n % 2 == 0) return n == 2;

      // Now, we have that n is odd and n >= 3.

      // Try to divide n by every odd number i, starting from 3
      for (int i = 3;; i += 2) {
        // We only have to try i up to the square root of n
        if (i > n / i) break;

        // Now, we have i <= n/i < n.
        // If n is divisible by i, n is not prime.
        if (n % i == 0) return false;
      }

      // n has no integer factor in the range (1, n), and thus is prime.
      return true;
    }

    以上是一个检测是否输入是质数的函数。

    C++
    // Tests negative input.
    TEST(IsPrimeTest, Negative) {
      // This test belongs to the IsPrimeTest test case.

      EXPECT_FALSE(IsPrime(-1));
      EXPECT_FALSE(IsPrime(-2));
      EXPECT_FALSE(IsPrime(INT_MIN));
    }

    // Tests some trivial cases.
    TEST(IsPrimeTest, Trivial) {
      EXPECT_FALSE(IsPrime(0));
      EXPECT_FALSE(IsPrime(1));
      EXPECT_TRUE(IsPrime(2));
      EXPECT_TRUE(IsPrime(3));
    }

    // Tests positive input.
    TEST(IsPrimeTest, Positive) {
      EXPECT_FALSE(IsPrime(4));
      EXPECT_TRUE(IsPrime(5));
      EXPECT_FALSE(IsPrime(6));
      EXPECT_FALSE(IsPrime(9));
      EXPECT_TRUE(IsPrime(23));
      EXPECT_FALSE(IsPrime(25));
    }

    如上的测试用例中,考虑到负数,一些特殊的数(0,1,2,3)以奇偶数的判断。实际开发中代码要比这个复杂很多。要提高代码覆盖率,很多时候需要借助fake,mock等手段来完成。

    四. 断言

    ASSERT_*系列的断言

          当断言失败时,退出当前函数。

    EXPECT_*系列的断言

            当断言失败时,继续向下执行。

    所有的断言都支持 << 操作:

    C++
      EXPECT_TRUE(IsPrime(0)) << " input is 0";
      EXPECT_FALSE(IsPrime(1)) << " input is 1";

    在断言失败后,打印信息将包含<<输出的内容。

    1. 显式成功失败   

    SUCCEED() 标记成功。实际上并不产生任何操作,在代码中起到类似注释的作用。

    Fatal assertion

    Nonfatal assertion

    FAIL()

    ADD_FAILURE()

    FAIL类似于ASSERT_*断言,会退出当前函数。ADD_FAILURE会继续向下执行。

    C++
    switch(expression) {
      case 1:
        ... some checks ...
      case 2:
        ... some other checks ...
      default:
        FAIL() << "We shouldn't get here.";
    }

            一般在一些测试分支中,可能用到以上断言。

    2. 条件断言

    Fatal assertion

    Nonfatal assertion

    Verifies

    ASSERT_TRUE(condition);

    EXPECT_TRUE(condition);

    condition is true

    ASSERT_FALSE(condition);

    EXPECT_FALSE(condition);

    condition is false

    3. 二元比较

    Fatal assertion

    Nonfatal assertion

    Verifies

    ASSERT_EQ(expected,actual);

    EXPECT_EQ(expected,actual);

    expected == actual

    ASSERT_NE(val1,val2);

    EXPECT_NE(val1,val2);

     val1 != val2

    ASSERT_LT(val1,val2);

    EXPECT_LT(val1,val2);

    val1 < val2

    ASSERT_LE(val1,val2);

    EXPECT_LE(val1,val2);

    val1 <= val2

    ASSERT_GT(val1,val2);

    EXPECT_GT(val1,val2);

    val1 > val2

    ASSERT_GE(val1,val2);

    EXPECT_GE(val1,val2);

    val1 >= val2

    4. 字符串比较

    Fatal assertion

    Nonfatal assertion

    Verifies

    ASSERT_STREQ(expected_str, actual_str);

    EXPECT_STREQ(expected_str, actual_str);

    the two C strings have the same content

    ASSERT_STRNE(str1, str2);

    EXPECT_STRNE(str1, str2);

    the two C strings have different content

    ASSERT_STRCASEEQ(expected_str, actual_str);

    EXPECT_STRCASEEQ(expected_str, actual_str);

    the two C strings have the same content, ignoring case

    ASSERT_STRCASENE(str1, str2);

    EXPECT_STRCASENE(str1, str2);

    the two C strings have different content, ignoring case

    5. 浮点数相等比较

    Fatal assertion

    Nonfatal assertion

    Verifies

    ASSERT_FLOAT_EQ(expected, actual);

    EXPECT_FLOAT_EQ(expected, actual);

    the two float values are almost equal

    ASSERT_DOUBLE_EQ(expected, actual);

    EXPECT_DOUBLE_EQ(expected, actual);

    the two double values are almost equal

         相近比较

    Fatal assertion

    Nonfatal assertion

    Verifies

    ASSERT_NEAR(val1, val2, abs_error);

    EXPECT_NEAR(val1, val2, abs_error);

    the difference between val1 and val2 doesn’t exceed the given absolute error

        可以自行设置abs_error来进行比较。

    6. 异常断言

    Fatal assertion

    Nonfatal assertion

    Verifies

    ASSERT_THROW(statement,exception_type)

    EXPECT_THROW(statement,exception_type)

    Verifies that statement throws an exception of type exception_type.

    ASSERT_ANY_THROW(statement)

    EXPECT_ANY_THROW(statement)

    Verifies that statement throws an exception of any type.

    ASSERT_NO_THROW(statement)

    EXPECT_NO_THROW(statement)

    Verifies that statement does not throw any exception.

    C++
    int Div(int m, int n) {
      if (n == 0) {
        throw std::runtime_error("n can't be 0.\n");

      }
      return m / n;
    }

    比如我希望检测以上的代码是否可以正常输出异常。

    C++
    TEST(DivTest, exception) {
        EXPECT_THROW({
                Div(10, 0);
                },
                std::runtime_error
                );
        EXPECT_NO_THROW({
                Div(10, 10);
                });
            
    }

    7. 谓词断言

      EXPECT_PRED*

    Fatal assertion

    Nonfatal assertion

    Verifies

    ASSERT_PRED1(pred,val1)

    EXPECT_PRED1(pred,val1)

    Verifies that the predicate pred returns true when passed the given values as arguments.

    ASSERT_PRED2(pred,val1,val2)

    EXPECT_PRED2(pred,val1,val2)

    ASSERT_PRED3(pred,val1,val2,val3)

    EXPECT_PRED3(pred,val1,val2,val3)

    ASSERT_PRED4(pred,val1,val2,val3,val4)

    EXPECT_PRED4(pred,val1,val2,val3,val4)

    ASSERT_PRED5(pred,val1,val2,val3,val4,val5)

    EXPECT_PRED5(pred,val1,val2,val3,val4,val5)

    这些可以代替EXPECT_TRUE。pred是函数名,val*是输入的参数。好处是当输入的参数可以打印出来。

    C++
    TEST(MutuallyPrimeTest, positive) {
        int a = 4;
        int b = 10;
        EXPECT_TRUE(MutuallyPrime(a, b));
        EXPECT_PRED2(MutuallyPrime, a, b);
    }

    比如以上的输出是:

    Bash
    [ RUN      ] MutuallyPrimeTest.positive
    D:\code\googletest-main\googletest\samples\sample1_unittest.cc(158): error: Value of: MutuallyPrime(a, b)
      Actual: false
    Expected: true
    D:\code\googletest-main\googletest\samples\sample1_unittest.cc(159): error: MutuallyPrime(a, b) evaluates to false, where
    a evaluates to 4
    b evaluates to 10
    [  FAILED  ] MutuallyPrimeTest.positive (0 ms)

          使用EXPECT_PRED2可以打印出a,b的值。

    EXPECT_PRED_FORMAT*

    Fatal assertion

    Nonfatal assertion

    Verifies

    ASSERT_PRED_FORMAT1(pred_formatter,val1)

    EXPECT_PRED_FORMAT1(pred_formatter,val1)

    Verifies that the predicate pred_formatter succeeds when passed the given values as arguments.

    ASSERT_PRED_FORMAT2(pred_formatter,val1,val2)

    EXPECT_PRED_FORMAT2(pred_formatter,val1,val2)

    ASSERT_PRED_FORMAT3(pred_formatter,val1,val2,val3)

    EXPECT_PRED_FORMAT3(pred_formatter,val1,val2,val3)

    ASSERT_PRED_FORMAT4(pred_formatter,val1,val2,val3,val4)

    EXPECT_PRED_FORMAT4(pred_formatter,val1,val2,val3,val4)

    ASSERT_PRED_FORMAT5(pred_formatter,val1,val2,val3,val4,val5)

    EXPECT_PRED_FORMAT5(pred_formatter,val1,val2,val3,val4,val5)

    与EXPECT_PRED*的差别是,pred_for_matter需要返回一个testing::AssertionResult对象。也就是说这里不能直接调用准备测试的函数,而是要另外实现一个测试函数:

     要测试的函数:

    C++
    // Returns the smallest prime common divisor of m and n,
    // or 1 when m and n are mutually prime.
    int SmallestPrimeCommonDivisor(int m, int n) { ... }

    // Returns true if m and n have no common divisors except 1.
    bool MutuallyPrime(int m, int n) { ... }

        测试代码:

    C++
    // A predicate-formatter for asserting that two integers are mutually prime.
    testing::AssertionResult AssertMutuallyPrime(const char* m_expr,
                                                 const char* n_expr,
                                                 int m,
                                                 int n) {
      if (MutuallyPrime(m, n)) return testing::AssertionSuccess();

      return testing::AssertionFailure() << m_expr << " and " << n_expr
          << " (" << m << " and " << n << ") are not mutually prime, "
          << "as they have a common divisor " << SmallestPrimeCommonDivisor(m, n);
    }

    ...
    const int a = 3;
    const int b = 4;
    const int c = 10;
    ...
    EXPECT_PRED_FORMAT2(AssertMutuallyPrime, a, b);  // Succeeds
    EXPECT_PRED_FORMAT2(AssertMutuallyPrime, b, c);  // Fails

    需要再实现一个AssertMutuallyPrime,用来返回 AssertionResult 对象。

    8. 死亡断言

    Fatal assertion

    Nonfatal assertion

    Verifies

    ASSERT_DEATH(statement,matcher)

    EXPECT_DEATH(statement,matcher)

    Verifies that statement causes the process to terminate with a nonzero exit status and produces stderr output that matches matcher.

    ASSERT_DEATH_IF_SUPPORTED(statement,matcher)

    EXPECT_DEATH_IF_SUPPORTED(statement,matcher)

    If death tests are supported, behaves the same as EXPECT_DEATH. Otherwise, verifies nothing.

    ASSERT_DEBUG_DEATH(statement,matcher)

    EXPECT_DEBUG_DEATH(statement,matcher)

    In debug mode, behaves the same as EXPECT_DEATH. When not in debug mode (i.e. NDEBUG is defined), just executes statement.

    ASSERT_EXIT(statement,predicate,matcher)

    EXPECT_EXIT(statement,predicate,matcher)

    Verifies that statement causes the process to terminate with an exit status that satisfies predicate, and produces stderr output that matches matcher.

    一个例子:

    C++
    void exittest() {
            std::cerr << "my error" << std::endl;
            exit(1);
    }

    TEST(MyDeathTest, exit) {
            EXPECT_DEATH(exittest(), "my error");
    }

    一些注意点:

          1. gtest文档里要求死亡测试的test_suit_name,定义为*DeathTest的形式。gtest会将死亡测试拍到最前面,最先运行。

            2.  EXPECT_DEATH 中的matcher也需要与代码中的错误信息匹配。gtest中提供了正则表达式用于匹配。需要的话,可以参考其文档。

      3. 死亡测试的基本原理是,gtest会启动一个进程来运行死亡测试。

    9. HRESULT断言

    Fatal assertion

    Nonfatal assertion

    Verifies

    ASSERT_HRESULT_SUCCEEDED(expression)

    EXPECT_HRESULT_SUCCEEDED(expression)

    Verifies that expression is a success HRESULT.

    ASSERT_HRESULT_FAILED(expression)

    EXPECT_HRESULT_FAILED(expression)

    Verifies that expression is a failure HRESULT.

    针对windows系统中的HRESULT的断言。

    10. 泛化的断言

    Fatal assertion

    Nonfatal assertion

    Verifies

    ASSERT_THAT(value,matcher)

    EXPECT_THAT(value,matcher)

    Verifies that value matches the matcher matcher.

    这里的matcher可用来对比字符串,正则表达式等内容。

    C++
    using ::testing::AllOf;
    using ::testing::Gt;
    using ::testing::Lt;
    using ::testing::MatchesRegex;
    using ::testing::StartsWith;

    ...
    EXPECT_THAT(value1, StartsWith("Hello"));
    EXPECT_THAT(value2, MatchesRegex("Line \\d+"));
    ASSERT_THAT(value3, AllOf(Gt(5), Lt(10)));

    五. TEST_F

    之前的例子中,都是用TEST来写测试用例。更多的时候是用TEST_F来实现,TEST_F引入了类。

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

      void TearDown() override {}

      Queue q0_;
      Queue q1_;
      Queue q2_;
    };

    C++
    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;
    }

    1. 这里构建了QueueTest 继承于Test。这里有函数SetUp和TearDown可以帮助构建一些通用的数据成员。
    1. 使用TEST_F构建测试用例

    C++
    #define TEST_F(test_fixture, test_name) GTEST_TEST_F(test_fixture, test_name)

       TEST_F的展开与TEST类似,比如 TEST_F(QueueTest, IsEmptyInitially)展开后的的代码大致如下:

    C++
      class QueueTest_IsEmptyInitially_Test
          : public QueueTest {

    而 TEST_F(QueueTest, DequeueWorks) 展开后:

    C++
      class QueueTest_DequeueWorks_Test
          : public QueueTest {

    可以看到每个测试用例,其实都生成一个类,共同点是都继承自QueueTest,所以可以使用基类的一些共同资源。所以两个用例不能共享运行状态,比如 TEST_F(QueueTest, IsEmptyInitially) 中新增或者删除了元素,并不会对 TEST_F(QueueTest, DequeueWorks) 产生影响。

    六. TEST_P

    文档里描述的TEST_P的用法有三种,分别看一下。

    1. 程序中可以输入不同的参数

    C++
    bool Foo(const std::string& input) {
        if (input == "cat") {
            return true;
        } else if (input == "dog") {
            return true;
        }
         else {
            return false;
         }
    }

    比如一段函数可以输入不同的参数,但一些参数的意思相同(比如大小写?)。这个例子不太好,不过大概是那个意思。

    C++
    class FooTest : public testing::TestWithParam {

    };

              要使用TEST_P,需要继承 TestWithParam。或者

    C++
    class FooTest : public testing::Test,
                    public testing::WithParamInterface {
    };

            这里只是简单使用,FooTest中不需要其它代码。

    C++
    TEST_P(FooTest, Foo) { EXPECT_TRUE(Foo(GetParam())); }

    INSTANTIATE_TEST_SUITE_P(MyFooTest, FooTest,
                             testing::Values("cat", "dog", "rabbit"));

          与TEST和TEST_T不同,TEST_P并不将用例直接加入运行列表,只负责定义类。实例化需要通过INSTANTIATE_TEST_SUITE_P 来完成。

    Bash
    [----------] 3 tests from MyFooTest/FooTest
    [ RUN      ] MyFooTest/FooTest.Foo/0
    [       OK ] MyFooTest/FooTest.Foo/0 (0 ms)
    [ RUN      ] MyFooTest/FooTest.Foo/1
    [       OK ] MyFooTest/FooTest.Foo/1 (0 ms)
    [ RUN      ] MyFooTest/FooTest.Foo/2
    D:\code\googletest-main\googletest\samples\sample1_unittest.cc(189): error: Value of: Foo(GetParam())
      Actual: false
    Expected: true
    [  FAILED  ] MyFooTest/FooTest.Foo/2, where GetParam() = "rabbit" (0 ms)
    [----------] 3 tests from MyFooTest/FooTest (6 ms total)

    [----------] Global test environment tear-down
    [==========] 4 tests from 2 test suites ran. (129 ms total)
    [  PASSED  ] 3 tests.
    [  FAILED  ] 1 test, listed below:
    [  FAILED  ] MyFooTest/FooTest.Foo/2, where GetParam() = "rabbit"

     1 FAILED TEST

           运行结果如上。大致过程是 TEST_P(FooTest, Foo) 中通过GetParam()得到testing::Values("cat", "dog", "rabbit") 提供的值,每个值运行一次。

    2. 针对不同的策略

         比如说有两个函数:

    C++
    // Returns true if and only if n is a prime number.
    bool IsPrime(int n) {
      // Trivial case 1: small numbers
      if (n <= 1) return false;

      // Trivial case 2: even numbers
      if (n % 2 == 0) return n == 2;

      // Now, we have that n is odd and n >= 3.

      // Try to divide n by every odd number i, starting from 3
      for (int i = 3;; i += 2) {
        // We only have to try i up to the square root of n
        if (i > n / i) break;

        // Now, we have i <= n/i < n.
        // If n is divisible by i, n is not prime.
        if (n % i == 0) return false;
      }

      // n has no integer factor in the range (1, n), and thus is prime.
      return true;
    }

    C++
    bool IsPrimeFast(int n) {
        if (n <= 1) return false;

        for (int i = 2; i * i <= n; i++) {
        // n is divisible by an integer other than 1 and itself.
        if ((n % i) == 0) return false;
        }

        return true;
    }

    都用来求质数。一套测试case应该可以满足两个函数,可以这样实现:

    C++
    class MyPrimeTest : public testing::TestWithParam> {

        void SetUp() override { fun_ = GetParam(); }
        void TearDown() override {
        }

        protected:
          std::function fun_;

    };

    // Tests negative input.
    TEST_P(MyPrimeTest, Negative) {
        // This test belongs to the IsPrimeTest test case.

        EXPECT_FALSE(fun_(-1));
        EXPECT_FALSE(fun_(-2));
        EXPECT_FALSE(fun_(INT_MIN));
    }

    // Tests some trivial cases.
    TEST_P(MyPrimeTest, Trivial) {
        EXPECT_FALSE(fun_(0));
        EXPECT_FALSE(fun_(1));
        EXPECT_TRUE(fun_(2));
        EXPECT_TRUE(fun_(3));
    }

    INSTANTIATE_TEST_SUITE_P(MyTwoPrimeTest, MyPrimeTest,
                             testing::Values(IsPrime, IsPrimeFast));

           这里IsPrime和IsPrimeFast作为std::function对象传入。在MyPrimeTest的SetUp中通过GetParam可以分别得到两个std::function对象。然后TEST_P中通过fun_来调用,就可以为两个函数使用同一套测试用例。

           这个例子不太好,可以参考gtest中提供的例子,为不同的派生类使用同一套用例。

    3. 不同的输入,拥有相同的结果。

    文档里给的说明是数据驱动模型,不过没有例子。感觉起来和第一种情形类似吧。

    七. Fake & Mock

           实际开发过程中,一些运行条件并不容易满足。比如说一段程序是从摄像头中读取数据,并检测图像是否存在被遮挡,模糊,冻结等情形。测试程序接一个摄像头并不是一个好的解决方法,而且也无法控制摄像头产生遮挡,模糊等情形的图像。有的时候是我们需要借助第三方库进行开发,测试用例针对的是我们自身开发的代码,并不需要关注第三方库的内部逻辑。

    这时候我们可以应用fake&mock的方式来写测试代码。gtest中强调fake和mock的区别,我的理解是借助于gtest中提供的gmock库实现的就是mock,其它实现就是fake。为此,我们需要先理解mock。

    1. 构建mock类

    C++
    class Turtle {
      ...
      virtual ~Turtle() {}
      virtual void PenUp() = 0;
      virtual void PenDown() = 0;
      virtual void Forward(int distance) = 0;
      virtual void Turn(int degrees) = 0;
      virtual void GoTo(int x, int y) = 0;
      virtual int GetX() const = 0;
      virtual int GetY() const = 0;
    };

    Turtle代表实际环境中要用到的一个类,我们要维护的代码中用到了该类。测试用例中,希望不要用到它,这时候可以构建mock类:

    C++
    #include "gmock/gmock.h"  // Brings in gMock.

    class MockTurtle : public Turtle {
     public:
      ...
      MOCK_METHOD(void, PenUp, (), (override));
      MOCK_METHOD(void, PenDown, (), (override));
      MOCK_METHOD(void, Forward, (int distance), (override));
      MOCK_METHOD(void, Turn, (int degrees), (override));
      MOCK_METHOD(void, GoTo, (int x, int y), (override));
      MOCK_METHOD(int, GetX, (), (const, override));
      MOCK_METHOD(int, GetY, (), (const, override));
    };

           这里主要用到的是MOCK_METHOD。

    C++
      MOCK_METHOD(ReturnType, MethodName, (Args...));
      MOCK_METHOD(ReturnType, MethodName, (Args...), (Specs...));

    MOCK_METHOD可以接收3个或者4个参数,前三个函数对应于函数类型。Specs可以是const,override,noexcept,Calltype和ref, 其实也是针对于函数的定义。

    2. 使用mock对象

    C++
    #include "path/to/mock-turtle.h"
    #include "gmock/gmock.h"
    #include "gtest/gtest.h"

    using ::testing::AtLeast;                         // #1

    TEST(PainterTest, CanDrawSomething) {
      MockTurtle turtle;                              // #2
      EXPECT_CALL(turtle, PenDown())                  // #3
          .Times(AtLeast(1));

      Painter painter(&turtle);                       // #4

      EXPECT_TRUE(painter.DrawCircle(0, 0, 10));      // #5
    }

    构建mock类后,就可以在测试代码中使用它。以上代码中,构建了MockTurtle对象,这段代码的主要测试对象是Painter,所以需要用turtle构建Painter对象。

          EXCEPT_CALL表示在painter.DrawCircle运行过程中,希望可以调用到turtle对象中的PenDown函数。 Times表示调用到次数,这里的AtLeast(1)表示最少一次。

          这里给出的例子中Painter是通过构造函数中传入指针的方式来使用Turtle对象。不过实际代码中很多时候要mock的对象可能是在函数内部构建的,也有可能是通过外部参数得到的。我的解决方法是为Painter增加一个SetXXX函数,将mock对象设置进去(可以通过宏控制之只在测试代码SetXXX才生效)。

    3. 设置期望

     a. 基本语法

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

    用一个例子来看看它的意思:

    C++
    EXPECT_CALL(turtle, GetX())
        .Times(5)
        .WillOnce(Return(100))
        .WillOnce(Return(150))
        .WillRepeatedly(Return(200));

    表示期望调用turtle中的GetX函数,Times表示调用5次。第一次的返回值是100,第二次是150,后面都返回200。

    b. Matchers : 希望使用什么参数

           一些情况下,我们需要确定传入到mock对象的参数是否正确。

    C++
    // Expects the turtle to move forward by 100 units.
    EXPECT_CALL(turtle, Forward(100));

            比如我们希望调用Forward时传入的参数是100。

    C++
    using ::testing::_;
    ...
    // Expects that the turtle jumps to somewhere on the x=50 line.
    EXPECT_CALL(turtle, GoTo(50, _));

    只对部分参数感兴趣,可以只用占位符。

    C++
    using ::testing::Ge;
    ...
    // Expects the turtle moves forward by at least 100.
    EXPECT_CALL(turtle, Forward(Ge(100)));

    或者需要输入参数满足一定的条件。

    C++
    // Expects the turtle to move forward.
    EXPECT_CALL(turtle, Forward);
    // Expects the turtle to jump somewhere.
    EXPECT_CALL(turtle, GoTo);

    当不关心输入参数时。

    c. Cardinalities : 希望调用多少次  

            之前的例子中已经看到过,可以通过Times来指定希望调用多少次。

            WillOnce 以可以代替Times的工作,当有1个WillOnce的时候(没有WillRepeatedly)表示期望调用一次。当有n个WillOnce时,期望调用n次。

       没有Times和willOnce的时候,表示不关心调用次数。

    d. Action: 希望函数做什么

          类似之前的例子:

    C++
    using ::testing::Return;
    ...
    EXPECT_CALL(turtle, GetY())
         .WillOnce(Return(100))
         .WillOnce(Return(200))
         .WillRepeatedly(Return(300));

    通过Return,可以指定函数被调用后的返回值。

    C++
    using testing::ReturnPointee;
    ...
      int x = 0;
      MockFoo foo;
      EXPECT_CALL(foo, GetValue())
          .WillRepeatedly(ReturnPointee(&x));  // Note the & here.
      x = 42;
      EXPECT_EQ(42, foo.GetValue());  // This will succeed now.

         可以返回指针。

    C++
    using ::testing::ReturnRef;

    class MockFoo : public Foo {
      ...
      MOCK_METHOD(Bar&, GetBar, (), (override));
      MOCK_METHOD(const Bar&, GetBar, (), (const, override));
    };

    ...
      MockFoo foo;
      Bar bar1, bar2;
      EXPECT_CALL(foo, GetBar())         // The non-const GetBar().
          .WillOnce(ReturnRef(bar1));
      EXPECT_CALL(Const(foo), GetBar())  // The const GetBar().
          .WillOnce(ReturnRef(bar2));

    可以返回引用。在不同的模式下,可以返回不同的值。

    e. 可以设定期望的调用次序

    C++
    using ::testing::InSequence;
    ...
    TEST(FooTest, DrawsLineSegment) {
      ...
      {
        InSequence seq;

        EXPECT_CALL(turtle, PenDown());
        EXPECT_CALL(turtle, Forward(100));
        EXPECT_CALL(turtle, PenUp());
      }
      Foo();
    }

    通过InSequence实现。

    4. 关于Fake

    大部分时候,Mock可以帮助我们实现测试功能。但某些情形下,可能Mock并不容易实现。比如说测试程序需要被调用的Mock函数sleep一段时间,因为外部函数要做时间统计,对于函数超时要做特殊处理。这时候可以通过Fake类来实现:

    C++
    class FakeTurtle : public Turtle {
    ........
      void PenUp() override {
         sleep(100);
      }
    .........
    };

        比如以上代码,继承Turtle后,可以在继承的函数中做一些自己的处理。

        这里无法使用mock中的Times等函数。但在一些环境下,也是一种有效的实现方式。实现起来方便灵活,而且可以在FakeTurtle中增加新的变量函数等,实现不同的要求。

  • 相关阅读:
    怎么给二维码添加文字或logo?快速美化二维码的使用技巧
    软件测试技术题目大全【含答案】
    前端工作总结276-控制change
    webpack高级应用篇(十五):基于 Promise 的动态 Remote,让模块联邦版本化
    【云原生】Dokcer安装Jenkins
    【GPU高性能编程CUDA实战】中的头文件下载
    Hexo在多台电脑上提交和更新
    Go 代码中的文档和注释
    tailwindcss -原子化 CSS 框架
    Java 泛型中的通配符
  • 原文地址:https://blog.csdn.net/junguo/article/details/128155373