对于单元测试,使用Gtest、Gmock 等工具提升代码覆盖率学习总结。

Unitest

代码覆盖率(Code coverage)是软件测试中的一种度量,描述程序中源代码被测试的比例和程度,所得比例称为代码覆盖率。

单元测试主要包括三方面测试:行覆盖,分支覆盖,功能覆盖:

行覆盖 = 已执行的语句数目 / 所有语句的总数目 * 100%;

分支覆盖 = 已执行的分支数目 / 分支总数目 * 100%;

功能覆盖 = 已执行的功能数目 /功能总数目 * 100%;

Google Test

Google Test 是一个测试框架,允许开发人员在 C++中编写和运行单元测试。它是由 Google 开发的,作为其开源软件项目的一部分,现在在软件开发社区中被广泛使用。Google Test 提供了一种简单高效的方法来为代码的单个单元编写测试,例如类或函数,并验证它们是否正确。它还提供了报告测试结果和管理测试套件的工具。

GTest、Gmock

标题

GTest、Gmock 学习

简介

GTest 和 Gmock 是 Google 开发的一套 C++测试框架,用于单元测试和模拟对象。它们可以帮助您编写可重复、可靠和可扩展的测试用例,并简化代码的调试。本文档将介绍如何使用 GTest 和 Gmock 进行单元测试和对象模拟。

使用 GTest 进行单元测试

1. 创建 GTest 项目:使用 CMake 或其他构建系统创建一个 GTest 项目,并在您的代码中包含 GTest 头文件和库文件。

2. 编写测试用例:定义一个公共的测试函数,并使用 GTest 提供的断言和测试宏来编写测试用例。

3. 运行测试:使用 GTest 提供的命令行工具或集成开发环境(IDE)来运行测试用例。

4. 分析测试结果:检查测试用例的执行结果,并通过断言失败来定位代码中的问题。

使用 Gmock 进行对象模拟

1. 创建 Gmock 项目:使用 CMake 或其他构建系统创建一个 Gmock 项目,并在您的代码中包含 Gmock 头文件和库文件。

2. 定义模拟对象:创建一个继承自 Gmock 提供的 Mock 类的模拟对象,并定义其期望的行为。

3. 使用模拟对象进行测试:在测试用例中使用模拟对象代替实际的对象,并验证其行为是否符合预期。

4. 分析测试结果:检查模拟对象的行为是否符合预期,并通过模拟对象的回调函数来验证其内部逻辑。

断言

基本断言

失败时中断执行的断言

失败时不中断执行的断言

断言成功情况

ASSERT_TRUE(condition);

EXPECT_TRUE(condition);

condition为真

ASSERT_FALSE(condition);

EXPECT_FALSE(condition);

condition为假

普通比较型断言

失败时中断执行的断言

失败时不中断执行的断言

断言成功情况

ASSERT_EQ(val1, val2);

EXPECT_EQ(val1, val2);

val1 == val2

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

C字符串比较型断言

 str1和str2是两个C字符串,以下断言对它们的值进行比较;如果要比较两个std::string对象,直接用之前 提到的EXPECT_NE,EXPECT_NE等。

失败时中断执行的断言

失败时不中断执行的断言

断言成功情况

ASSERT_STREQ(str1,str2);

EXPECT_STREQ(str1,str2);

这两个C字符串具有相同的内容

ASSERT_STRNE(str1,str2);

EXPECT_STRNE(str1,str2);

两个C字符串的内容不同

ASSERT_STRCASEEQ(str1,str2);

EXPECT_STRCASEEQ(str1,str2);

忽略大小写,两个C字符串的内容相同

ASSERT_STRCASENE(str1,str2);

EXPECT_STRCASENE(str1,str2)

忽略大小写,两个C字符串的内容不同

例如:char *str1 = "ABC";char *str2 = "ABC";,EXPECT_STREQ(str1, str2);断言通过, 因为它们的内容一样;而EXPECT_EQ(str1, str2);断言失败,因为它们的地址不一样。

注意:一个NULL指针和一个空字符串""是不同的。

浮点数比较型断言

  val1和val2是两个浮点数,以下断言对其进行比较。

失败时中断执行的断言

失败时不中断执行的断言

断言成功情况

ASSERT_FLOAT_EQ(val1, val2);

EXPECT_FLOAT_EQ(val1, val2);

这两个float值几乎相等

ASSERT_DOUBLE_EQ(val1, val2);

EXPECT_DOUBLE_EQ(val1, val2);

这两个double值几乎相等

  以下断言可以选择可接受的误差范围:

失败时中断执行的断言

失败时不中断执行的断言

断言成功情况

ASSERT_NEAR(val1, val2, abs_error);

EXPECT_NEAR(val1, val2, abs_error);

val1和val2的差的绝对值不超过abs_error

异常断言

这些断言验证一段代码是否抛出(或不抛出)给定类型的异常:

失败时中断执行的断言

失败时不中断执行的断言

断言成功情况

ASSERT_THROW(statement, exception_type);

EXPECT_THROW(statement, exception_type);

statement抛出给定类型的异常

ASSERT_ANY_THROW(statement);

EXPECT_ANY_THROW(statement);

statement抛出任何类型的异常

ASSERT_NO_THROW(statement);

EXPECT_NO_THROW(statement);

statement不抛出任何异常

分支覆盖率提升,主要涉及了模版、虚函数等。

SetUp() 和 TearDown() 确实是单元测试框架中的两个成员函数,这些功能常见于像 Google Test 这样的框架。这些函数通常用于测试用例或测试套件的设置(setup)和拆解(teardown)工作。让我们来看一下它们的用途:

SetUp()

函数 SetUp() 通常在每个测试用例之前自动执行。它的主要目的是为即将运行的测试用例准备测试环境。这可能涉及到:

  • 初始化变量。
  • 分配/构造测试所需的对象。
  • 配置虚拟环境、设置预期值、打桩(stubbing)或模拟(mocking)。
  • 打开需要的文件或数据库连接。

通过在 SetUp() 中做准备工作,可以确保每个测试用例启动时都具有清洁且一致的状态,从而使每个测试结果都是独立的。

TearDown()

函数 TearDown() 通常在每个测试用例执行完毕后自动执行。它用于清理 SetUp() 期间设置的状态。这可能包括:

  • 释放 SetUp() 中分配的资源。
  • 销毁构造的对象,释放内存。
  • 关闭文件或数据库连接。
  • 撤销测试中创建的任何变临时更改。

通过使用 TearDown() 清理,确保这次测试不会对后续的测试用例产生副作用,例如释放 SetUp() 中分配的任何动态内存,这有助于防止资源泄漏。

使用示例(以 Google Test 为例)

class MyTestFixture : public ::testing::Test {
protected:
    // 定义需要使用的成员变量和对象

    virtual void SetUp() override {
        // 设置代码
    }

    virtual void TearDown() override {
        // 清理代码
    }
};

// 使用测试用例
TEST_F(MyTestFixture, Test1) {
    // 这个测试会先运行 SetUp,然后是测试代码,最后运行 TearDown
}

在 Google Test 中,SetUp() 和 TearDown() 被定义在派生自 ::testing::Test 的测试套件类中,并且可以被重写以提供所需的功能。然后你可以使用 TEST_F 宏来写具体的测试用例。这里 F 指的是 Fixture,代指的就是上面定义的测试套件类 MyTestFixture。