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);
// <TechnicalDetails> // // 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. // // </TechnicalDetails> }
// 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<FactorialTest_Zeor_Test>) ;
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<TestSuite*> test_suites_; 在 TestSuite 中有一下定义了 test_info_list_ // The vector of TestInfos in their original order. It owns the // elements in the vector. std::vector<TestInfo*> test_info_list_; |
在程序启动阶段,TEST定义的所有信息就会按照以上的形式保存到对应的vecotr中。
回看一下MakeAndRegisterTestInfo中的最后一个参数 new TestFactoryImpl<FactorialTest_Zeor_Test>。这里构建了一个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 TestClass> 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;< |