Google unit test framework gtest -- value parameter test

To test a method, you need more parameters to test, such as maximum value, minimum value, abnormal value and normal value. There will be more repetitive code work in the middle, and the value parameter test is to avoid this repetitive work and will not lose the convenience and accuracy of the test.

For example, testing a function requires some parameters for boundary testing. The following case tests whether a number is a prime number and needs to test various parameters.

Method 1:

class Prime {
 public:
  bool IsPrime(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;
  }
};
​
TEST(IsPrimeTest, Negative) {
  Prime prime;
  EXPECT_FALSE(prime.IsPrime(-5));
  EXPECT_FALSE(prime.IsPrime(-1));
}
​
TEST(IsPrimeTest, Trivial) {
  Prime prime;
  EXPECT_FALSE(prime.IsPrime(0));
  EXPECT_FALSE(prime.IsPrime(1));
  EXPECT_TRUE(prime.IsPrime(2));
  EXPECT_TRUE(prime.IsPrime(3));
}

The output result is:

The above tests are not complete, but there are many duplicate codes. For example, each test case needs to create a prime and write an EXPECT statement.

Method 2: using testfixture, you can reduce the operation of creating prime class. The above test creation prime is the initialization of the data to be tested. If the initialization environment is complex, using test fixrule can greatly improve the efficiency and ensure that the running conditions of each test are the same.

class PrimeTest : public ::testing::Test {
 protected:
  Prime prime;
};
​
TEST_F(PrimeTest, Negative) {
  EXPECT_FALSE(prime.IsPrime(-5));
  EXPECT_FALSE(prime.IsPrime(-1));
}
​
TEST_F(PrimeTest, Trivial) {
  EXPECT_FALSE(prime.IsPrime(0));
  EXPECT_FALSE(prime.IsPrime(1));
  EXPECT_TRUE(prime.IsPrime(2));
  EXPECT_TRUE(prime.IsPrime(3));
}

The output result is:

Method 2 is much better than method 1, but it still does not change the problem of code duplication. Continue to improve and use loops.

Method 3: use a loop to eliminate duplicate code.

class PrimeTest : public ::testing::Test {
 protected:
  Prime prime;
};
​
TEST_F(PrimeTest, Negative) {
  auto vec = std::vector<int>{-5, -1, 0, 1};
  for (auto v : vec) {
    EXPECT_FALSE(prime.IsPrime(v));
  }
}
​
 TEST_F(PrimeTest, Trivial) {
  auto vec2 = std::vector<int>{2, 3, 5, 7};
  for_each(vec2.begin(), vec2.end(), [&](int a) { 
      EXPECT_TRUE(prime.IsPrime(a));
      }
    );
}

The output result is:

Method 3 eliminates copying code statements and can complete the test of parameters of the same type. However, there is a big problem. Many parameters share a test case. If a parameter fails, the code cannot indicate which test failed.

// For example, the first use case has a parameter written incorrectly
TEST_F(PrimeTest, Negative) {
  auto vec = std::vector<int>{-5, -1, 0, 2};
  for (auto v : vec) {
    EXPECT_FALSE(prime.IsPrime(v));
  }
}

The result of the code is an error in the first test, which can only prompt primetest Negative failed. If there are many parameters in the parameter list, it is not easy to check where it fails, which violates the basic principle of testing.

[ RUN      ] PrimeTest.Negative
D:\PROJECTS\googletest\googletest\samples\sample6_unittest.cc(297): error: Value of: prime.IsPrime(v)
  Actual: true
Expected: false
[  FAILED  ] PrimeTest.Negative (1 ms)

For example, in the tests of methods 1 and 2, the following detailed error prompts can be given for the same error.

[ RUN      ] PrimeTest.Negative
D:\PROJECTS\googletest\googletest\samples\sample6_unittest.cc(277): error: Value of: prime.IsPrime(2)
  Actual: true
Expected: false
[  FAILED  ] PrimeTest.Negative (1 ms)

Method 4: Fortunately, gtest provides a solution, which can not only avoid repeated code, but also run the test cases of each parameter separately for each test, and accurately report the error location after error.

class PrimeTest : public ::testing::TestWithParam<int> {
 protected:
  Prime prime;
};
​
TEST_P(PrimeTest, ReturnsFalseForNonPrimes) {
  int n = GetParam();
  EXPECT_FALSE(this->prime.IsPrime(n));
}
​
INSTANTIATE_TEST_CASE_P(myParmTest,    // Instance name
                        PrimeTest,     // Test case name
                        testing::Values(-5,0,1,4));  // Type list

First, PrimeTest inherits from testwithparm < int >; This is equivalent to creating a test suite.

Second, use Test_P create a test case. The first parameter is the name of the test fixture class and the second parameter is the name of the test case. In the test case, you can use GetParam to obtain each parameter, use this pointer and use Prime class instance.

Third, register the test case, install_ TEST_ CASE_ The first parameter of P macro is the name of the test, and the second parameter is the name of test fixue or test case. The third parameter is the parameter list that needs to be input into the case to run, and values is used to receive the list data.

The output results are shown in the figure below. values(-5,0,1,4) has four parameters in total and runs four tests.

Compared with method 3, if a data test is wrong, you can prepare to report the error message. For example, testing::Values(-5,0,1,5)), the last parameter is written as 5, and the output is as follows. Prompt that the last use case test3, parameter 5, failed to run

  • myParmTest/PrimeTest.ReturnsFalseForNonPrimes/3, where GetParam() = 5 (1 ms)

[==========] Running 4 tests from 1 test case.
[----------] Global test environment set-up.
[----------] 4 tests from myParmTest/PrimeTest
[ RUN      ] myParmTest/PrimeTest.ReturnsFalseForNonPrimes/0
[       OK ] myParmTest/PrimeTest.ReturnsFalseForNonPrimes/0 (0 ms)
[ RUN      ] myParmTest/PrimeTest.ReturnsFalseForNonPrimes/1
[       OK ] myParmTest/PrimeTest.ReturnsFalseForNonPrimes/1 (0 ms)
[ RUN      ] myParmTest/PrimeTest.ReturnsFalseForNonPrimes/2
[       OK ] myParmTest/PrimeTest.ReturnsFalseForNonPrimes/2 (0 ms)
[ RUN      ] myParmTest/PrimeTest.ReturnsFalseForNonPrimes/3
D:\PROJECTS\googletest\googletest\samples\sample6_unittest.cc(320): error: Value of: this->prime.IsPrime(n)
  Actual: true
Expected: false
[  FAILED  ] myParmTest/PrimeTest.ReturnsFalseForNonPrimes/3, where GetParam() = 5 (1 ms)
​

Method 5: the above method 4 has an obvious disadvantage. The parameters in the test::Values list can only be the numbers that meet the test case conditions, that is, all the numbers that test false. If you need some prime numbers and the test result is true, you need to write another test case. Obviously, this is not a good design. You can consider passing the test result true or false as a parameter to a test case, In this way, the testing of prime and non prime numbers can be realized in a test case. The specific implementation is also very simple. Testwithparam < T >, when t is a combination number, the above goal is achieved.

class PrimeTest : public ::testing::TestWithParam<std::pair<int, bool>>{
 protected:
  Prime prime;
};
​
TEST_P(PrimeTest, ReturnsFalseForNonPrimes) {
  auto parm = GetParam();
  ASSERT_EQ(this->prime.IsPrime(parm.first), parm.second);
}
​
INSTANTIATE_TEST_CASE_P(myParmTest,                     
                        PrimeTest,                     
                        testing::Values(std::make_pair(-5, false),
                            std::make_pair(-5, false),
                            std::make_pair(0, false),
                            std::make_pair(1, false),
                            std::make_pair(4, false),
                            std::make_pair(2, true),
                            std::make_pair(3, true),
                            std::make_pair(5, true)
                            ));

The output results are as follows: 8 test cases, 5 false and 3 true tests:

If a parameter test fails, the test error message can be clearly output.

[ RUN      ] myParmTest/PrimeTest.ReturnsFalseForNonPrimes/3
D:\PROJECTS\googletest\googletest\samples\sample6_unittest.cc(342): error: Expected equality of these values:
  this->prime.IsPrime(parm.first)
    Which is: true
  parm.second
    Which is: false
[  FAILED  ] myParmTest/PrimeTest.ReturnsFalseForNonPrimes/3, where GetParam() = (11, false) (1 ms)

 

Respect the technical articles, please indicate the reprint!

Google unit test framework gtest -- value parameter test

https://www.cnblogs.com/pingwen/p/14459873.html

Tags: C++ Testing

Posted by sankaty on Thu, 14 Apr 2022 15:51:54 +0930