KaiwuDBkaiwudb logo

KaiwuDB 技术博客专区

一文掌握 Google Test 框架

2023-10-12







一、简介





1. 引言

在开发过程中,如何保证代码的质量以及程序的正确性成为了我们亟需解决的问题,其中测试用例成为了不必可少的一部分。测试用例不仅可以帮助我们验证代码的正确性,还能帮助我们捕获潜在的错误,提高代码的可靠性和可维护性。然而手动编写和执行测试用例可能会非常繁琐和耗时,因此我们引入了 Google Test 框架。

2. Google Test 框架

Google Test (gtest) 是一个由 Google 开发的流行的 C++ 单元测试框架。它为开发者提供了一套强大的工具和功能,用于编写、组织和运行测试用例。gtest 框架支持测试驱动开发(Test-Driven Development,TDD)和行为驱动开发(Behavior-Driven Development,BDD)方法。

具有以下主要功能和特点:
简单易用

gtest 框架提供了简洁而直观的语法和 API,使得编写测试用例变得简单易懂。通过使用宏和断言,开发者可以轻松地定义测试用例和验证预期结果;

丰富的断言

gtest 框架提供了一组丰富的断言宏,用于验证测试结果。这些断言宏包括比较值、检查条件、抛出异常等功能,可以满足各种测试需求;

灵活的测试组织

gtest 框架允许开发者将测试用例组织成测试套件(Test Suite),并可以嵌套多个测试套件。这样可以更好地组织和管理测试用例,提高测试代码的可读性和可维护性;

测试夹具支持

gtest 框架提供了测试夹具(Test Fixture 的概念,允许开发者在多个测试用例之间共享资源或设置环境。通过派生测试夹具类并重写 SetUp() 和 TearDown() 方法,开发者可以方便地进行资源的初始化和清理操作;

完善的编译及平台支持

gtest 框架支持 C++11 及以上的 C++ 标准,支持 Linux、MacOS、Windows 等众多平台,支持 gcc5.0+、clang5.0+、MSVC2015+ 编译器,支持 Bazel 以及 cmake 构建工具;

丰富的测试报告

gtest 框架生成详细的测试报告,显示每个测试用例的运行结果,包括通过的用例和失败的用例。如果断言失败,报告会提供失败的原因和位置,方便开发者快速定位问题。




二、Google Test 框架集成





1. 系统安装 gtest 测试框架

Ubuntu:
sudo apt install libgtest-dev

Centos:
https://pkgs.org/search/?q=gtest # 下载gtest-devel.rpm
rpm -ivh gtest-devel.rpm     # 安装rpm包
2. CMake 项目集成

Cmakelists.txt 文件中添加以下代码:

enable_testing() # 开启测试,否则无法执行make test
add_executable(add_test_exe add_test.cpp) # 添加可执行程序
target_link_libraries(add_test_exe PRIVATE gtest pthread) # 链接gtest库
add_test(add_test_exe ${CMAKE_BINARY_DIR}/add_test_exe) # 添加测试,保证make test可以执行该测试用例


3. 简单 Demo

add_test.cpp:

#include <gtest/gtest.h>

int add(int a, int b) {  
    return a + b;
}

TEST(TEST_ADD, UNSIGNED_INT_VALUE) {    
    int result = add(100, 200);    
    EXPECT_EQ(result, 300);    
    result = add(200, 300);    
    EXPECT_NE(result, 400);
}

int main() {  
    testing::InitGoogleTest();  
    return RUN_ALLTESTS();
}


4. 编译运行

~/Projects/Test master +625 !5592 ?1 ) mkdir build && cd build && cmake .. && make  -j4
-- The C compiler identification is GNU 9.4.0
-- The CXX compiler identification is GNU 9.4.0
-- Check for working C compiler:/usr/bin/cc
-- Check for working CXX compiler:/usr/bin/c++
-- Configuring done
-- Generating done
-- Build files have been written to:/home/limstor/Projects/Test/build
Scanning dependencies of target add_test_exe
[50%] Building CXX object CMakeFiles/add_test_exe.dir/aadd_test.cpp.o
[100%] Linking CXX executable add_test_exe
[100%] Built target add_test_exe
~/Projects/Test/build master +625 !5592 ?2)/add_test_exe
[==========] Running 1 test from 1 test suite.
[----------] Global test environment set-up.
[----------] 1 test from TEST_ADD
[ RUN      ]TEST_ADD.UNSIGNED_INT_VALUE
[       OK ]TEST_ADD.UNSIGNED_INT_VALUE (O ms)
[----------] 1 test from TEST_ADD (0 ms total)

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




三、Google Test - 测试宏





1. TEST 宏

说明:该宏定义用来测试其内部代码,其内部断言决定最终的测试结果。

使用方式:

TEST(TestSuiteName, TestName) {  
    ... statements ...
}



用途:针对多个用例之间不需要进行数据共用的测试场景。

示例:

int add(int a, int b) {  
    return a + b;
}
TEST(TEST_ADD, UNSIGNED_INT_VALUE) {  
  int result = add(100, 200);  
  EXPECT_EQ(result, 300);  
  result = add(200, 300);  
  EXPECT_NE(result, 400);
}

TEST(TEST_ADD, NEGATIVE_INT_VALUE) {  
  int result = add(-100, -200);  
  EXPECT_EQ(result, -300);  
  result = add(-200, -300);  
  EXPECT_NE(result, -400);
}


2. TEST_F 宏

说明:该宏定义用来对 TestFixtureName 类进行多样测试。

使用方法:

TEST_F(TestFixtureName, TestName) {  
  ... statements ...
}


用途:针对多个用例之间需要进行数据共用的测试场景,用于多样测试,也有助于简化测试代码。

使用示例:

class Student {
public:  
  Student(int id, std::string name): id_(id), name_(name) {};  
  ~Student() = default;  
  void SetAge(int age) { age_ = age; }  
  int GetAge() const { return this->age_; }  
  void SetScore(int score) { score_ = score; }  
  int GetScore() const { return this->score_; }
private:  
  int id_;  
  std::string name_;  
  int age_;  
  int score_;
};
class StudentTest : public testing::Test {
protected:  
  void SetUp() override {    
      student = new Student(1234, "Tom");  
  }  
  void TearDown() override {    
    delete student;  
  }  
  Student* student;
};
TEST_F(StudentTest, SET_AGE_TEST) {  
  student->SetAge(16);  
  int age = student->GetAge();  
  EXPECT_EQ(age, 16);
}
TEST_F(StudentTest, SET_SCORE_TEST) {  
  student->SetScore(99);  
  int score = student->GetScore();  
  ASSERT_EQ(score, 99);
}int main() {  
  testing::InitGoogleTest();  
  return RUN_ALL_TESTS();
}

3. TEST_P 宏

说明:该宏定义用来参数化测试。

使用方式:

TEST_P(TestFixtureName, TestName) {  
  ... statements ...
}


用途:当待测试方法的行为取决于传入的参数时,而且这些参数的不同组合有多种, 而你又不想为此写多个类似的 test case 时,可以用参数化测试。

示例:

struct TestData {  
  int  a;  
  int  b;  
  int  result;  
  char type;
};
class CalculateTest : public ::testing::TestWithParam<TestData> {
protected:  
  void checkData() {    
      int a = GetParam().a;    
    int b = GetParam().b;   
    int result = GetParam().result;    
    switch (GetParam().type) {      
      case '+':        
        EXPECT_EQ(a + b, result);        
        break;      
      case '-':        
        EXPECT_EQ(a - b, result);        
        break;      
      case '*':        
        EXPECT_EQ(a * b, result);        
        break;      
      case '/':        
        EXPECT_EQ(a / b, result);        
        break;      
      default:        
        break;    
    }  
    }
};
TEST_P(CalculateTest, Test) {  
    checkData();
}
INSTANTIATE_TEST_SUITE_P(TestMyClassParams,                         
             CalculateTest,                         
             ::testing::Values(                           
             TestData{100, 200, 300, '+'},                           
             TestData{20, 5, 15, '-'},                           
             TestData{5, 6, 30, '*'},                           
             TestData{8, 2, 3, '/'}                         
             ));
int main() {  
    testing::InitGoogleTest();  
    return RUN_ALL_TESTS();
}




四、Google Test - 断言





断言:对测试代码中的检查点进行检查。分为以下两种断言类型:

· ASSERT_*断言:当检查点失败时,退出当前函数;
· EXPECT_*断言:当检查点失败时,继续往下执行。

1. 布尔值检查

1.png


2. 字符串类型检查

2.png

3. 数值类型检查

3.png

4. 返回成功或失败

4.png

5. 返回成功或失败

5.png

6. 谓词断言

6.png

7. 浮点数检查

7.png


8. Windows 平台的 HRESULT 检查



8.png

9. 类型检查

testing::StaticAssertTypeEq(); 类似模板中的编译检查

除以上 9 种断言使用方法外,还可以在断言后增加自定义信息输出
EXPECT_EQ(a, b) << "Vectors x and y differ at index ";




五、Google Test - 事件机制





gtest 提供了多种事件机制:
· 全局事件:所有用例执行前后;
· TestSuite 级别:同一 TestSuite 的第一个用例前,最后一个用例执行后;
· TestCase 级别:每个测试用例前后。

1. 全局事件

要实现全局事件,必须写一个类,继承 testing::Environment 类,实现里面的 SetUp 和 TearDown 方法:
· SetUp() 方法在所有用例执行前执行;
· TearDown() 方法在所有用例执行后执行。

示例:


class FooEnvironment : 
public testing::Environment {public:  
  virtual void SetUp() {            
    std::cout << "Foo FooEnvironment SetUP" << std::endl;      
  }      
  virtual void TearDown() {            
    std::cout << "Foo FooEnvironment TearDown" << std::endl;      
  }
};


注意:在执行用例之前,需要在 main 函数中通过 testing::AddGlobalTestEnvironment 方法将事件挂载进来。



示例:


int main() {      
  testing::AddGlobalTestEnvironment(new FooEnvironment);      
  testing::InitGoogleTest();      
  return RUN_ALL_TESTS();}


2. TestSuite 事件


要实现 TestSuite 事件,必须写一个类,继承 testing::Test 类,实现 SetUpTestCase 和 TearDownTestCase 方法:


· SetUpTestCase() 方法在 TestSuite 里的第一个 TestCase 之前执行;
· TearDownTestCase() 方法在 TestSuite 里的最后一个 TestCase 之后执行。


示例:
class FooTest : public testing::Test { 
  protected:    
    static void SetUpTestCase() {    
    shared_resource_ = new int(1234);    
  }    
  static void TearDownTestCase() {        
    delete shared_resource_;        
    shared_resource_ = NULL;    
  }    
  int* shared_resource_;
};


3. TestCase 事件


实现方法和 TestSuite 事件几乎一样,只不过它是实现的 SetUp 和 TearDown 方法:
(1) SetUp() 方法在每一个 TestCase 之前执行;
(2) TearDown() 方法每一个 TestCase 之后执行。

示例:
class FooCalcTest:public testing::Test {
protected:    
  virtual void SetUp() {      
      m_foo.Init();        
  }        
  virtual void TearDown() {          
    m_foo.Finalize();        
  }        
  FooCalc m_foo;
};




六、Google Test - 命令行参数






在测试用例二进制文件后执行以下参数

1. --gtest_list_tests

使用这个参数时,将不会执行里面的测试用例,而是输出测试用例的列表。

2. --gtest_filter

对执行的测试用例进行过滤,支持通配符。
     ?    单个字符
     *    任意字符
     -    排除,如,-a 表示除了 a
     :    取或,如,a:b 表示 a 或 b

比如下面的例子:


./foo_test 没有指定过滤条件,运行所有用例
./foo_test --gtest_filter=* 使用通配符*,表示运行所有用例
./foo_test --gtest_filter=FooTest.* 运行所有 TestSuiteName 为 FooTest 的用例
./foo_test --gtest_filter=*Null*:*Constructor* 运行所有TestSuiteName或 TestName 包含 Null 或 Constructor 的用例。
./foo_test --gtest_filter=FooTest.*-FooTest.Bar 运行所有 TestSuiteName 为 FooTest 的用例,但是除了 FooTest.Bar 这个用例。


3. --gtest_also_run_disabled_tests



执行用例时,同时也执行被置为无效的测试用例(TestSuiteName 或 TestName 以 DISABLED 为前缀)。

4. --gtest_repeat=[COUNT]

设置用例重复运行次数,例如:


--gtest_repeat=1000  重复执行1000次,即使中途出现错误。
--gtest_repeat=-1  无限次数执行
--gtest_repeat=1000 
--gtest_break_on_failure  重复执行1000次,并且在第一个错误发生时立即停止。这个功能对调试非常有用。
--gtest_repeat=1000 --gtest_filter=FooBar  重复执行1000次测试用例名称为FooBar的用例。


5. --gtest_color=(yes|no|auto)


输出命令行时是否使用一些五颜六色的颜色。默认是 auto。

6. --gtest_print_time

输出命令行时是否打印每个测试用例的执行时间。默认是不打印。

7. --gtest_output=xml[:DIRECTORY_PATH\|:FILE_PATH]

将测试结果输出到一个 xml 中:


· --gtest_output=xml:  不指定输出路径时,默认为用例当前路径;

· --gtest_output=xml:~/dir   指定输出到某个目录;

· --gtest_output=xml:~/dir/foo.xml  指定输出到~/dir/foo.xml。



如果不是指定了特定的文件路径,gtest 每次输出的报告不会覆盖,而会以数字后缀的方式创建。

8. --gtest_break_on_failure

调试模式下,当案例失败时停止,方便调试。

9. --gtest_throw_on_failure

当用例失败时以 C++ 异常的方式抛出。

10. --gtest_catch_exceptions

是否捕捉异常。gtest 默认是不捕捉异常的,因此假如你的测试用例抛了一个异常,很可能会弹出一个对话框,这非常的不友好,同时也阻碍了测试用例的运行。如果想不弹框,可以通过设置这个参数来实现。如将 --gtest_catch_exceptions 设置为一个非零的数。

注意:这个参数只在 Windows 平台下有效


免费体验 KaiwuDB 全新功能

立即体验

关于我们
联系我们

KaiwuDB B站

KaiwuDB
B站

KaiwuDB 微信公众号

KaiwuDB
微信公众号

© 上海沄熹科技有限公司 Shanghai Yunxi Technology Co., Ltd.    沪ICP备2023002175号-1
400-624-5688-7
1V1 方案咨询
marketing@kaiwudb.org.cn