当前位置: 代码网 > it编程>编程语言>C/C++ > 【C++】Google Test(gtest)单元测试

【C++】Google Test(gtest)单元测试

2024年08月06日 C/C++ 我要评论
测试夹具(Test Fixture)用于提供一个环境,允许开发者在多个测试用例之间共享设置和清理的代码,确保每个测试用例都在相同或可控的初始状态下运行。是一种软件测试方法,它旨在将应用程序的各个部分(通常是方法或函数)分离出来并独立测试,以确保每个部分都能够按预期工作。为测试用例的名称,用于将相关的测试分组在一起,以便在测试结果中更容易地识别和归类。味前缀的致命断言,其在测试失败时程序立即终止。为前缀的非致命断言,其在测试失败时程序会继续执行;为具体测试的名称,一般描述测试的目的。方法中,初始化了一个。

google test(gtest)单元测试

单元测试是一种软件测试方法,它旨在将应用程序的各个部分(通常是方法或函数)分离出来并独立测试,以确保每个部分都能够按预期工作。

gtest是google公司发布的一款开源的c/c++单元测试框架。gtest的test 宏用于定义单个测试用例,其基本语法为:

test(testcasename, testname) {
  // 测试代码
}

其中 testcasename为测试用例的名称,用于将相关的测试分组在一起,以便在测试结果中更容易地识别和归类。testname为具体测试的名称,一般描述测试的目的。

每个测试用例包含一个或多个检查点,这些检查点使用断言来验证代码的行为。包括以expect_ 为前缀的非致命断言,其在测试失败时程序会继续执行;和以 assert_ 味前缀的致命断言,其在测试失败时程序立即终止。基本的非致命断言包括:

  • expect_eq(val1, val2):检查 val1 == val2
  • expect_ne(val1, val2):检查 val1 != val2
  • expect_lt(val1, val2):检查 val1 < val2
  • expect_le(val1, val2):检查 val1 <= val2
  • expect_gt(val1, val2):检查 val1 > val2
  • expect_ge(val1, val2):检查 val1 >= val2

对应的致命断言:

  • assert_eq(val1, val2)
  • assert_ne(val1, val2)
  • assert_lt(val1, val2)
  • assert_le(val1, val2)
  • assert_gt(val1, val2)
  • assert_ge(val1, val2)

除此之外,还有专门用于字符串比较的断言:

  • expect_streq(str1, str2):检查 str1str2 是相同的字符串。
  • expect_strne(str1, str2):检查 str1str2 是不同的字符串。
  • expect_strcaseeq(str1, str2):检查 str1str2 是相同的字符串,忽略大小写。
  • expect_strcasene(str1, str2):检查 str1str2 是不同的字符串,忽略大小写。

用于浮点数比较的断言:

  • expect_float_eq(val1, val2):检查 val1val2 具有相同的浮点值。
  • expect_double_eq(val1, val2):检查 val1val2 具有相同的双精度值。
  • expect_near(val1, val2, abs_error):检查 val1val2 之间的差值在 abs_error 范围内。

用于布尔值的断言:

  • expect_true(condition):检查 condition 为真。
  • expect_false(condition):检查 condition 为假。

使用示例

项目结构:

gtest_demo/
├── cmakelists.txt
├── include/
│   └── math_functions.h
├── src/
│   └── math_functions.cpp
└── tests/
    └── test_math_functions.cpp

cmakelists.txt

# 指定cmake的最低版本
cmake_minimum_required(version 3.10)

# 定义项目名称
project(gtest_demo)

# 设置c++标准为c++11,并且为强制要求
set(cmake_cxx_standard 11)
set(cmake_cxx_standard_required true)

# 添加当前项目的include目录,以便编译器能找到头文件
include_directories(${project_source_dir}/include)

# 添加源文件,生成一个名为math_functions的静态库
add_library(math_functions src/math_functions.cpp)

# 查找google test库,确保系统已安装gtest
find_package(gtest required)
# 添加gtest的include目录
include_directories(${gtest_include_dirs})

# 添加测试源文件,生成一个名为runtests的可执行文件
add_executable(runtests tests/test_math_functions.cpp)

# 链接math_functions, gtest库和pthread库到可执行文件runtests
# gtest框架在实现上使用了多线程(pthread)来管理测试并发执行
target_link_libraries(runtests ${gtest_libraries} pthread math_functions)

# 启用测试功能
enable_testing()
# 添加一个名为runtests的测试
add_test(name runtests command runtests)

include/math_functions.h

#ifndef math_functions_h // 头文件保护
#define math_functions_h

int add(int a, int b);
int subtract(int a, int b);
float add(float a, float b);
double add(double a, double b);

#endif 

src/math_functions.cpp :

#include "math_functions.h"

int add(int a, int b) {
    return a + b + 1; // 故意错误
}

int subtract(int a, int b) {
    return a - b;
}

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

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

tests/test_math_functions.cpp:

#include <gtest/gtest.h>
#include "math_functions.h"

// 测试add函数(整数)
test(mathfunctionstest, addint) {
    expect_eq(add(1, 1), 2);
    expect_eq(add(-1, -1), -2);
    expect_eq(add(0, 0),2); 
}

// 测试subtract函数
test(mathfunctionstest, subtract) {
    expect_eq(subtract(2, 1), 1);
    expect_eq(subtract(-1, -1), 0);
    expect_eq(subtract(0, 0), 0); 
}

// 测试add函数(浮点数)
test(mathfunctionstest, addfloat) {
    expect_float_eq(add(0.1f, 0.2f), 0.3f);
    expect_near(add(0.1f, 0.2f), 0.3f, 1e-6);
}

test(mathfunctionstest, adddouble) {
    expect_double_eq(add(0.1, 0.2), 0.3);
    expect_near(add(0.1, 0.2), 0.3, 1e-6);
}

int main(int argc, char **argv) {
    ::testing::initgoogletest(&argc, argv);// 初始化 google test 
    return run_all_tests();  // 运行所有测试用例
}

编译和运行测试

    mkdir build
    cd build
    cmake..
    make
    ./runtest
[==========] running 4 tests from 1 test case.
[----------] global test environment set-up.
[----------] 4 tests from mathfunctionstest
[ run      ] mathfunctionstest.addint
/home/hrn/cppprojects/gtest_demo/tests/test_math_functions.cpp:6: failure
      expected: add(1, 1)
      which is: 3
to be equal to: 2
/home/hrn/cppprojects/gtest_demo/tests/test_math_functions.cpp:7: failure
      expected: add(-1, -1)
      which is: -1
to be equal to: -2
/home/hrn/cppprojects/gtest_demo/tests/test_math_functions.cpp:8: failure
      expected: add(0, 0)
      which is: 1
to be equal to: 0
[  failed  ] mathfunctionstest.addint (0 ms)
[ run      ] mathfunctionstest.subtract
[       ok ] mathfunctionstest.subtract (0 ms)
[ run      ] mathfunctionstest.addfloat
[       ok ] mathfunctionstest.addfloat (0 ms)
[ run      ] mathfunctionstest.adddouble
[       ok ] mathfunctionstest.adddouble (0 ms)
[----------] 4 tests from mathfunctionstest (0 ms total)

[----------] global test environment tear-down
[==========] 4 tests from 1 test case ran. (0 ms total)
[  passed  ] 3 tests.
[  failed  ] 1 test, listed below:
[  failed  ] mathfunctionstest.addint

 1 failed test

3个测试通过,1个不通过,add函数有误.

更多用法

测试夹具

测试夹具(test fixture)用于提供一个环境,允许开发者在多个测试用例之间共享设置和清理的代码,确保每个测试用例都在相同或可控的初始状态下运行。

在gtest中,测试夹具通常是通过派生自::testing::test类的子类来实现的,并通过test_f()宏定义测试用例。

示例:

#include <gtest/gtest.h>
#include <vector>

// 假设有一个简单的类 myclass
class myclass {
public:
    myclass(int data) : basevalue(data) {}
    void add(int data) { basevalue += data; }
    int getdata() const { return basevalue; }

private:
    int basevalue;
};

// 测试夹具类
class mytest : public ::testing::test {
protected:
    myclass *my;
    std::vector<int> sharedvector;

    // 在每个测试用例执行前设置环境
    void setup() override {
        my = new myclass(100);
        sharedvector = {1, 2, 3, 4, 5};
    }

    // 在每个测试用例执行后清理环境
    void teardown() override {
        delete my;
    }
};

// 使用 test_f() 宏编写测试用例
test_f(mytest, test1) {
    my->add(10);
    expect_eq(my->getdata(), 110);

    sharedvector.push_back(6);
    expect_eq(sharedvector.size(), 6);
    expect_eq(sharedvector.back(), 6);
}

test_f(mytest, test2) {
    my->add(100);
    expect_eq(my->getdata(), 200);

    sharedvector.pop_back();
    expect_eq(sharedvector.size(), 4);
    expect_eq(sharedvector.back(), 4);
}

test_f(mytest, test3) {
    my->add(-50);
    expect_eq(my->getdata(), 50);

    sharedvector[0] = 10;
    expect_eq(sharedvector[0], 10);
    expect_eq(sharedvector.size(), 5);
}

test_f(mytest, test4) {
    my->add(0);
    expect_eq(my->getdata(), 100);

    sharedvector.clear();
    expect_true(sharedvector.empty());
}

在这个示例中,测试夹具类 mytest 通过继承 ::testing::test 类,实现了 setup()teardown() 方法。在 setup() 方法中,初始化了一个 myclass 对象和一个 std::vector<int>。在 teardown() 方法中,清理了 myclass 对象。

每个测试用例 (test1test2test3test4) 都使用了相同的测试夹具 mytest,共享了初始化和清理代码。在每个测试用例中,myclass 对象和 sharedvector 都被重新初始化,以确保测试用例之间相互独立。

(0)

相关文章:

版权声明:本文内容由互联网用户贡献,该文观点仅代表作者本人。本站仅提供信息存储服务,不拥有所有权,不承担相关法律责任。 如发现本站有涉嫌抄袭侵权/违法违规的内容, 请发送邮件至 2386932994@qq.com 举报,一经查实将立刻删除。

发表评论

验证码:
Copyright © 2017-2025  代码网 保留所有权利. 粤ICP备2024248653号
站长QQ:2386932994 | 联系邮箱:2386932994@qq.com