GoogleTest : 测试框架(单元测试)

这篇具有很好参考价值的文章主要介绍了GoogleTest : 测试框架(单元测试)。希望对大家有所帮助。如果存在错误或未考虑完全的地方,请大家不吝赐教,您也可以点击"举报违法"按钮提交疑问。

GoogleTest

googletest: GoogleTest - Google Testing and Mocking Framework

googletest 是一个由 Google 的测试技术团队开发的测试框架,它考虑到了谷歌的特定需求和限制。无论你使用的是 Linux、Windows 还是 Mac,只要你编写 C++ 代码,googletest 都可以帮到你。它支持任何类型的测试,不只是单元测试。

  1. 独立的且可重复的(googletest 通过在不同的对象上运行每个测试用例来隔离测试。当测试失败时允许你单独运行它,以便快速调试);

  2. 可移植的且可复用的(googletest 适用于不同的操作系统,不同的编译器);

  3. 失败时,提供尽可能多的关于故障的信息(googletest 不会在第一个测试失败时停止。相反,它仅停止当前的测试并继续运行下一个);

  4. 得到良好的组织并反映测试代码的结构(googletest 将相关的测试分组为测试套件,它们可以共享数据和子例程);

  5. 测试框架将精力集中在测试内容上(googletest 自动追踪所有定义的测试,且无需用户以运行它们的顺序迭代它们);

  6. 快(通过 googletest,可以跨测试用例复用共享资源,且只支出一次 set-up/tear-down 的开销,不使测试相互依赖)。

任何可被送给 ostream 的都可以被送进断言的宏。

编译

gtest 提供了两种编译方式 bazelcmake,以 cmake 为例:

mkdir build && cd build 
cmake ..
# 只需要包含头文件 gtest.h或者gmock.h

断言

断言是 gtest 核心组成,所有的测试用例最终都是以断言实现的。

  • ASSERT 系列的断言如果失败,则会产生一个严重错误并导致当前作用范围的测试用例中断

  • EXPECT 不会产生任何错误,测试用例依旧可以继续执行

true / false 条件测试

致命 非致命 验证
ASSERT_TRUE(condition); EXPECT_TRUE(condition); condition: true
ASSERT_FALSE(condition); EXPECT_FALSE(condition); condition: false

失败时,ASSERT_* 产生一个致命错误,并从当前函数退出,EXPECT_* 则产生一个非致命错误,并允许函数继续运行。

二元比较

致命断言 非致命断言 验证
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 字符串ASSERT_EQ 测试它们是否位于相同内存位置,而不是是否具有相同的值。因此,比较 C 字符串的值使用 ASSERT_STREQ()

  • 当执行指针比较时使用 *_EQ(ptr, nullptr) 和 *_NE(ptr, nullptr) 而不是 *_EQ(ptr, NULL) 和 *_NE(ptr, NULL)

字符串比较

致命断言 非致命断言 验证
ASSERT_STREQ(str1, str2); EXPECT_STREQ(str1, str2); 具有相同内容
ASSERT_STRNE(str1, str2); EXPECT_STRNE(str1, str2); 具有不同内容
ASSERT_STRCASEEQ(str1, str2); EXPECT_STRCASEEQ(str1, str2); 相同内容忽略大小写
ASSERT_STRCASENE(str1, str2); EXPECT_STRCASENE(str1, str2); 不同内容忽略大小写

如果宽字符串(wchar_t*, Windows 上 UNICODE 模式的 TCHAR*,或 std::wstring)被送进断言,它将在打印时被转换为 UTF-8

断言扩展

SUCCEED() : 产生一个成功标识,只代表某一个步躁成功;

FAIL() : 产生一个严重错误,相当于一个 ASSERT 宏失败;

ASSERT_THROW(expre,type) : 断言表达式 expre 会抛出一个 type 类型异常;

ASSERTANY THROW(expre) : 断言表达式 expre 会抛出一个任意类型异常;

ASSERT_NO_THROW(expre) : 断言表达式 expre 不会抛出任何异常。

测试 TEST

  • 使用 TEST() 宏定义并命名一个测试函数(没有返回值的宏函数);

  • 解析命令行中的 GoogleTest 参数,它允许用户通过多样的命令行参数来控制测试程序的行为(即定制命令行参数的行为):testing::InitGoogleTest(&argc, argv);

  • 搜索不同的 Test Case 和不同的源文件中所有已经存在的测试案例然后运行它们,成功时返回 1,否则返回 0:return RUN_ALL_TESTS();

// 1. TestSuiteName 测试套件名
// 2. TestName 测试用例名
/// 名称不该包含下划线
TEST(FactorialTest, HandlesZeroInput) {
    EXPECT_EQ(Factorial(0), 1);
}
TEST(FactorialTest, HandlesPositiveInput) {
    EXPECT_NE(Factorial(1), 1);
} 

int main(int argc, char** argv) {
    testing::InitGoogleTest(&argc, argv);
    return RUN_ALL_TESTS();
}

Google C++ Style Guide 函数和类命名规则

全局事件

#include "gtest/gtest.h"
class TestFA2 : public testing::Environment {
public:
    void SetUp() override {
        std::cout << "SetUp" << std::endl;
    }
    void TearDown() override {
        std::cout << "TearDown" << std::endl;
    }
};
TEST(TestFA21, globalEnv) {
    EXPECT_EQ(3, 4) << "int compare";
}
int main(int argc, char** argv) {
    testing::InitGoogleTest(&argc, argv);
    testing::Environment* env = new TestFA2();
    testing::AddGlobalTestEnvironment(env);
    int ret = 0;
    ret     = RUN_ALL_TESTS();
    return ret;
}

测试夹具 TEST_F

  • 允许你为多个不同的测试复用相同的数据对象配置。

  • googletest 为多个测试复用相同的测试夹具。

成员函数

虚函数
  • virtual void SetUp():类似于构造函数,在 TEST_F 之前运行
  • virtual void TearDown():类似于析构函数,在 TEST_F 之后运行
静态函数
  • static void SetUpTestSuite():在第一个 TEST 之前运行;
  • static void TearDownTestSuite():在最后一个 TEST 之后运行。
全局事件

继承 testing::Environment

  • virtual void SetUp():在所有用例之前运行;
  • virtual void TearDown():在所有用例之后运行。

创建

  1. 创建一个公有继承::testing::Test 的类,进行 protected 类内部声明;

  2. 编写一个默认的构造函数或 SetUp() 函数为每个 test 准备对象;

  3. 编写一个析构函数或 TearDown() 函数释放 SetUp() 中分配的资源;

  4. 完善类资源信息,使用 TEST_F 宏,第一个参数填写新建夹具类名称。

由于 C++ 语法不允许在构造/析构函数中调用虚函数 SetUp/TearDown,建议优先使用构造/析构函数完成数据的初始化。

使用

  • 使用 TEST_F(类名, 测试夹具名称)
// TestFixtureName 测试夹具类名(_F:fixture)
TEST_F(TestFixtureName, TestName) {
    ... test body ...
}

通过 TEST_F() 定义的每个测试,googletest 将在运行时自动创建一个 全新的 测试夹具,立即通过 SetUp() 初始化它,运行测试,最后调用 TearDown() 清理资源,然后删除测试夹具。

具体类定义
template < typename E > // E is the element type.
class Queue {
public:
    Queue();
    void   Enqueue(const E& element);
    E*     Dequeue(); // Returns NULL if the queue is empty.
    size_t size() const;
    ...
};
夹具类定义
#include "gtest/gtest.h"
class TestFA : public ::testing::Test {
public:
    TestFA() {}
    ~TestFA() {}

protected:
    void SetUp() override {
        std::cout << "Reference count: " << deq.use_count() << std::endl;
        auto              p    = deq.get();
        auto              pxin = *p;
        std::deque< int > a(5, -1);
        std::cout << "1. size: " << pxin.size() << std::endl;
        pxin.swap(a);
        std::cout << "2. size:" << pxin.size() << std::endl;
    }
    void TearDown() override {
        deq.reset();
        deq = nullptr;
    }

public:
    std::shared_ptr< std::deque< int > > deq =
        std::make_shared< std::deque< int > >();
};
测试定义
  • 测试案例都来自同一个测试夹具
  • 不同的测试案例,拥有相互独立的个体,独占数据,不会相互影响。
TEST_F(TestFA, isEmptyOnB) {
    EXPECT_GE(deq.use_count(), 1);
    if (deq.use_count() != 0) {
        deq.reset();
    }
}
TEST_F(TestFA, isEmptyOnBIndependence) {
    EXPECT_GE(deq.use_count(), 1);
}
测试调用

定义测试后,通过 RUN_ALL_TESTS() 运行它们,如果所有测试都成功,返回 0,否则返回 1。

调用 RUN_ALL_TESTS() 宏时:

  1. 保存所有 googletest 标记的状态;

  2. 为第一个测试创建一个测试夹具对象;

  3. 通过 SetUp() 初始化;

  4. 在测试夹具对象上运行测试;

  5. 通过 TearDown() 清理测试夹具;

  6. 删除夹具,恢复所有的 googletest 标记的状态;

  7. 为下一个测试重复上述步骤。

#include "gtest/gtest.h"
int main(int argc, char** argv) {
    // 解析 googletest 标记的命令行参数,并移除所有已识别的标记
    testing::InitGoogleTest(&argc, argv);
    return RUN_ALL_TESTS();
}

调用 RUN_ALL_TESTS() 一次,多次调用将产生冲突。

接口测试 Gmock

Google C++单元测试框架GoogleTest—Google Mock简介–概念及基础语法 - 超超boy - 博客园

使用步骤

创建抽象类
  • 创建抽象接口函数;
class B {
public:
    virtual const int sub(int s1, const int s2) const = 0;
};
创建 Mock 类继承
  • 类返回作用域、参数作用域等,都需要进行描述;
#include "gmock/gmock.h"
class MockB : public B {
public:
    MOCK_METHOD(const int, sub, ( int, const int ), (const, override));
};
Mock 测试
  • EXPECT_CALL(mock_object, method(matchers)) // 对象、匹配调用函数
                            .Times(cardinality) // 调用次数
                            .WillOnce(action) // 被调用一次时的行为
                            .WillRepeatedly(action); // 重复调用
#include "B.h"
#include "gtest/gtest.h"
MockB b;
TEST(mock_sub, actionBSub) {
    EXPECT_CALL(b, sub)
        .Times(::testing::AtLeast(3)) // 3次需要3个WillOnce
        .WillOnce(::testing::Return(1))
        .WillOnce(::testing::Return(2))
        .WillOnce(::testing::Return(34));
    std::cout << b.sub(2, 1) << std::endl;
    std::cout << b.sub(3, 1) << std::endl;
    std::cout << b.sub(55, 44) << std::endl;
    std::cout << b.sub(34, 0) << std::endl; // 默认返回 0
} 

// 参数匹配器
TEST(mock_sub, expectCall) {
    EXPECT_CALL(b, sub(123, 123)).WillRepeatedly(Return(246));
    std::cout << b.sub(123, 1234) << std::endl; // 默认 0
    std::cout << b.sub(123, 123) << std::endl; // 符合,246,测试用例失效
    std::cout << b.sub(123, 123) << std::endl; // 默认 0
    EXPECT_CALL(b, sub(Gt(123), _))
        .WillOnce(Return(1234));               // 大于 123,和任意数值
    std::cout << b.sub(124, 123) << std::endl; // 符合,-99
    std::cout << b.sub(123, 2) << std::endl;   // 0
} 

// 顺序适配器
TEST(mock_multi_sub, callInSequence) {
    Sequence s1;
    EXPECT_CALL(b, multi).InSequence(s1).WillOnce(Return(222)); // 先
    EXPECT_CALL(b, sub).InSequence(s1).WillOnce(Return(333));   // 后
    std::cout << b.sub(124, 123) << std::endl; // 不符。为 0
    std::cout << b.multi(123, 2) << std::endl; // 先,222
    std::cout << b.sub(124, 123) << std::endl; // 后,333
}
EXPECT_CALL
  1. Times至少调用次数,小于则默认回调,如默认 int 返回 0;
  2. WillRepeatedly :重复调用,类似默认调用
  3. ::testing::Gt(a) :函数参数匹配,设置匹配器,如当大于 a 时则匹配调用;
  4. InSequence :指定顺序适配器 Sequence 顺序调用成员函数;

示例

示例1:web 请求

std::string chat_room::log() {
    std::string* response;
    this->requester->execute("request",
                             response); // web访问,结果存在response指针中
    return *response;
} 

class http_request {
public:
    virtual ~http_request() {}
    virtual bool execute(std::string request, std::string* response) = 0;
};
class mock_http_request : public http_request {
public:
    MOCK_METHOD(bool, execute, (std::string request, std::string* response),
                (override));
}; 

TEST(ChatRoomTest, log) {
    testing::NiceMock< mock_message_dao >
                      mock_dao; // 在下一部分会提到mock_message_dao
    mock_http_request mock_requester; // Mock对象
    std::string       response =
        "response"; // 期待调用函数的第二个参数将指向这个string对象
    EXPECT_CALL(mock_requester, execute)
        .WillRepeatedly(    // 每次调用都会(WillRepeatedly)执行
            testing::DoAll( // 每次执行包含多个行为
                testing::SetArgPointee< 1 >(
                    response), // 将传入参数指针变量response指向response
                testing::Return(true))); // 返回值为true
    chat_room cr = chat_room(
        &mock_dao, &mock_requester); // 将mock对象通过chat_room的constructor注入
    EXPECT_EQ(cr.log(), "response"); // 调用和Google Test断言
}

示例2:数据库访问

void chat_room::join(chat_participant_ptr participant) {
    participants_.insert(participant);
    std::vector< std::string > recent_msg_strs =
        this->dao->get_messages(); // 从数据库中获取历史消息
    for (std::string recent_msg_str : recent_msg_strs) {
        // 将每一个消息发送给该聊天参与者
        auto msg = chat_message();
        msg.set_body_string(recent_msg_str);
        participant->deliver(msg);
    }
} 

class message_dao {
public:
    virtual ~message_dao() {}
    virtual bool                       add_message(std::string m) = 0;
    virtual std::vector< std::string > get_messages()             = 0;
}; 

class mock_message_dao : public message_dao {
public:
    MOCK_METHOD(bool, add_message, (std::string m), (override));
    MOCK_METHOD(std::vector< std::string >, get_messages, (), (override));
};
EST(ChatRoomTest, join) {
    mock_message_dao mock_dao; // 创建mock对象(需要注入chat_room)
    http_request_impl requester; // 创建web访问对象(也需要注入chat_room)
    auto mock_p1 = std::make_shared< mock_chat_participant >();
    // 创建participant的mock指针
    EXPECT_CALL(mock_dao, get_messages)
        .WillOnce(testing::Return(
            std::vector< std::string >{ "test_1", "test_2", "test_3" }));
    // 指定get_messages调用的返回值
    EXPECT_CALL(*mock_p1, deliver).Times(3);
    // 指定deliver调用的次数
    chat_room cr = chat_room(&mock_dao, &requester);
    // 创建chat_room对象,注入dao和requester
    cr.join(mock_p1); // 调用
}

注意

  1. 测试套件名称、测试夹具名称、测试名称中不应该出现下划线( TEST(TestSuiteName, TestName),生成名为TestSuiteName_TestName_Test 的类);

  2. Uniteresting mock function call 警告 : 没有相应匹配的EXCEPT_CALL,Google Mock 会生成这个警告,使用 NiceMock 进行 mock 类对象实例化;

  3. Mock 一般用于虚函数,通过 TMock 来 Mock 非虚函数;

  4. 单元测试文件可以放在根目录下面专用的 tests 文件夹下;文章来源地址https://www.toymoban.com/news/detail-463533.html

到了这里,关于GoogleTest : 测试框架(单元测试)的文章就介绍完了。如果您还想了解更多内容,请在右上角搜索TOY模板网以前的文章或继续浏览下面的相关文章,希望大家以后多多支持TOY模板网!

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处: 如若内容造成侵权/违法违规/事实不符,请点击违法举报进行投诉反馈,一经查实,立即删除!

领支付宝红包 赞助服务器费用

相关文章

  • C++单元测试GoogleTest和GoogleMock十分钟快速上手(gtest&gmock)

    下载 安装 重要文件 googletest gtest/gtest.h libgtest.a libgtest_main.a 当不想写 main 函数的时候,可以直接引入 libgtest_main.a; 否则 googlemock gmock/gmock.h libgmock.a libgmock_main.a 一 .断言 gtest 中的断言分成两大类: ASSERT_* 系列:如果检测失败就直接退出 当前函数 EXPECT_* 系列:如果检测失败

    2024年02月06日
    浏览(30)
  • C++ 测试框架 GoogleTest 初学者入门篇 丙

    *以下内容为本人的学习笔记,如需要转载,请声明原文链接 微信公众号「ENG八戒」https://mp.weixin.qq.com/s/RIztusI3uKRnoHVf0sloeg 开发者虽然主要负责工程里的开发任务,但是每个开发完毕的功能都是需要开发者自测通过的,所以经常会听到开发者提起单元测试的话题。那么今天我就

    2023年04月15日
    浏览(36)
  • GoogleTest运行特定的测试用例

    通常GoogleTest的单元测试,直接执行,就全跑一遍,很耗时,有时候需要只测试某个case,怎么只运行某个测试用例呢? 先列出所有case: 然后执行特定case: 或者: 如果你的测试程序加这些后缀无效,可能是你的main函数写错了,把main函数删除,GoogleTest默认有main函数的,可以

    2024年02月11日
    浏览(48)
  • <c++开发>测试工具 -之-GoogleTest

    <c++开发>测试工具 -之-GoogleTest GoogleTest 是测试技术团队根据 Google 的特定要求和限制开发的测试框架。无论您在 Linux、Windows 还是 Mac 上工作,如果您编写 C++ 代码,GoogleTest 都可以为您提供帮助。它支持任何类型的测试,而不仅仅是单元测试。Google Test 是由 Google 推出的 C

    2024年02月12日
    浏览(29)
  • GoogleTest+VS code编译和编写简单测试用例

    在B站看了非常多Gtest的教学视频,CSDN上gtest博客也特别多,但是都非常陈旧或者根本不是用vscode。本篇目的在于,说明如何在vscode上编写简单单元测试。 软件:vscode 2023 下载googletset源码: git clone https://gitcode.net/mirrors/google/googletest.git 原repo:https://github.com/google/googletest下载特

    2024年02月07日
    浏览(30)
  • GoogleTest之高级主题

    GTEST_SKIP()会跳过测试程序,这在运行阶段根据条件执行测试非常有用。GTEST_SKIP()可以用在单个测试,或者是继承自::testing::Environment or ::testing::Test的class fixture中的SetUp()中。可以用GTEST_SKIP()输出信息,以下为官网示例 输出: 可以通过自定义函数,使用断言的输出流运算符打印

    2024年02月07日
    浏览(18)
  • (笔记)googletest在C语言项目中的使用指南

    参考: 关于EXPECT_CALL 关于TEST的断言宏 关于gtest的C语言封装 gtest+stub打桩 stub工具源码:https://github.com/coolxv/cpp-stub/tree/master/src C语言的项目需要做单元测试(需要满足基本的测试,如返回值,字符串比较等) 涉及多个模块的交互(需要做隔离,打桩,自制输入) 实现打桩,函

    2024年02月01日
    浏览(22)
  • GoogleTest从入门到入门,小白都能看懂的gtest详细教程

    单元测试 项目管理和技术管理中做单元测试,衡量一个软件是否正常的标准,良好的单元测试以及足够多的覆盖率,至少保证关键功能,关键业务的覆盖率接近100%。 gtest是谷歌公司发布的一个跨平台(Linux、Mac OS、Windows等)的C++单元测试框架,它提供了丰富的断言、致命和

    2024年02月07日
    浏览(38)
  • 【GTest】使用CMakeLitsts.txt构建Windows和Linux的跨平台GoogleTest项目(非常详细+亲测有效)

    👉博__主👈:米码收割机 👉技__能👈:C++/Python语言 👉公众号👈:测试开发自动化 👉专__注👈:专注主流机器人、人工智能等相关领域的开发、测试技术 Linux构建参考我的文章:【点击这里】 windows构建静态库参考我的文章:【点击这里】 (1)创建GtestAPI的文件夹,作为

    2024年02月07日
    浏览(43)
  • QTest 单元测试框架及单元测试思考

    在不同的公司和不同的项目上,常常会听到单元测试,但是真正能落实的确实寥寥无几,无非是在单元测试的开发时间和回报上模棱两可。 到底是否需要单元测试吗? 引用知乎观点如下: 第一个问题应该是,这个公司需要(覆盖率比较高的)测试么? 对于大部分公司来说,

    2023年04月08日
    浏览(68)

觉得文章有用就打赏一下文章作者

支付宝扫一扫打赏

博客赞助

微信扫一扫打赏

请作者喝杯咖啡吧~博客赞助

支付宝扫一扫领取红包,优惠每天领

二维码1

领取红包

二维码2

领红包