Windows上编译GTest并执行测试用例

0 背景

之前写代码一直有习惯于搭建LLT框架,作为测试防护网能够大大提升代码健壮性,有效避免修改引入,另一方面测试用例作为项目重要资产也应该有个地方可以归档,写入LLT用例里面还可以通过git上库。
当前项目中只有业务应用代码,还未有对应用例,为了后续开发以及测试方便,抽了点时间研究在windows下编译GTest并执行。

1 环境

本地项目编译/运行的相关环境如下所示:

编号名称版本
1操作系统Windows 11
2编译环境Qt 5.8
3编程语言C++14
4编译器版本VS2015 MSVC 19.0
5GTest版本V1.15.2

2 生成GTest动态库

2.1 下载最新GTest库

GTest源码可以到GitHub下载,取对应的Release版本即可。通过打开网页可以看到最新版本是1.15.2,我现在用的就是最新的。
在这里插入图片描述

如果本地配好Git环境直接可以用命令行操作方便快捷:

git clone https://github.com/google/googletest.git -b v1.15.2

如果遇到git clone链接超时可以参考这个博客进行配置,就两个命令,亲测有效。

2.2 编译GTest

2.2.1 cmake gtest

一般来说,编译常规操作如下:

mkdir build
cd build
cmake ..
make && make install

但是我在执行的过程中出现很多问题,例如在gtest默认生成是静态库而不是动态库,还有编出来的版本是x86而不是x64等等。正常来说普通方式就能够编译,由于不同环境之间差异,我本地编译一直失败,于是乎我才用gtest和gmock单独编译的方式

编译命令如下:

mkdir build
cd build
cmake -G "Visual Studio 14 2015 Win64" ..

这里我强制指定了用64位编译,是因为我Qt项目所有的编译选项都是64位的,包括其他依赖库,为了配套,我选择编译64位。
执行情况:
在这里插入图片描述
到这里就能执行成功。同样的操作对gmock进行编译。然后就能获得对应.sln文件。(这一步编译成功后,我尝试在上一层目录,即gtest、gmock同级目录下的build执行命令,也是可以成功的,但此时我的lib库已经编译成功且生效了,只能说还是走了一些弯路)

2.2.1.1 遇到问题:target参数不对

问题情况截图报错如下:
在这里插入图片描述
根据报错提示定位Cmakelist里面执行代码,发现有个参数一开始没有赋值:GOOGLETEST_VERSION,于是我在执行开端前面加上对它的赋值且进行版本打印:

set(GOOGLETEST_VERSION "v1.15.2")
message(STATUS "GOOGLETEST_VERSION: ${GOOGLETEST_VERSION}")

重新执行cmake,问题解决。

2.2.1.2 遇到问题:xxx thread 编译报错

原始问题描述有点记不清了,但是记得跟多线程有关系,这个主要是出现在编译gtest的场景下出现。gtest目录的cmakelist里面有专门的编译选项,如果项目中对多线程没有依赖,可以改成ON。
在这里插入图片描述

2.2.2 用VS2015编译依赖库

cmake执行成功后,build目录下会出现一个googletest-distribution.sln,用VS2015打开进行编译;
在这里插入图片描述
PS:如果配置管理器里面没有x64,需要手动新建。
编译成功后在lib目录下就能看到对应的依赖库啦:
在这里插入图片描述

3 依赖库部署

在Qt项目中新建一个目录,把对应的lib库文件放到里面,且在.pro文件中进行路径指定,让编译器能找到这些动态库。
PS:放在文件夹里面的不仅要有lib库,对应的dll文件也要放进去,不然会出现编译报错。dll在前面编译出来的bin目录下。
在.pro目录下添加依赖:

#头文件依赖路径
INCLUDEPATH += 本地路径/gtest/include/
#lib库
LIBS += -L本地路径/gtest/lib/ -lgmock -lgtest

如果不需要用到gmock,可以不把它加进去,根据自己项目情况实时调整。

3.1 遇到问题

令人头大的一个编译错误:error: LNK2001: 无法解析的外部符号 “class testing::internal::Mutex testing::internal::g_gmock_mutex” (?g_gmock_mutex@internal@testing@@3VMutex@12@A)
明明已经把lib库和dll库放进去了,但还是出现编译报错,网上查了资料,在这里找到了参考答案,于是乎得修改.pro文件,只需要添加一行即可解决:

DEFINES += GTEST_LINKED_AS_SHARED_LIBRARY=1

4 编写测试用例

需要先引用头文件:

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

如果编译报错找不到头文件,说明是头文件路径未指定或者是指定得不对,指定正确就能正常编译了。

测试框架:

class TestGTest : public ::testing::Test
{
public:
    void SetUp() override
    {
        // 初始化操作
    }

    void TearDown() override
    {
        // 结束后操作
    }
};

TEST_F(TestGTest , TestLoadCfg)
{
    // TODO:用例代码
}

如果需要对某个类的私有函数进行测试,可以考虑使用FRIEND_TEST,通过友元访问的方式,可以直接访问被测试类的私有函数和成员。当前我的项目中采用这种方式进行插桩测试。
项目中有个类需要经常调用另一个子类获取对应属性,于是乎用到gmock对这些属性进行插桩,代码框架如下:

class MockTestClient : public TestClient {
public:
    MOCK_METHOD(int, GetA, (), (const, override));
    MOCK_METHOD(int, GetB, (), (const, override));
    MOCK_METHOD(int, GetC, (), (const, override));
    MOCK_METHOD(int, GetD, (), (const, override));

    MockTestClient () = default;
    ~MockTestClient () override = default;
};

// 测试用例
TEST_F(TestGTest, TestOutPut)
{
    auto testClient = std::make_unique<MockTestClient >();
    EXPECT_CALL(*testClient , GetA()).WillOnce(::testing::Return(80));
    EXPECT_CALL(*testClient , GetB()).WillOnce(::testing::Return(1000));
    EXPECT_CALL(*testClient , GetC()).WillOnce(::testing::Return(500));
    EXPECT_CALL(*testClient , GetD()).WillOnce(::testing::Return(2000));

	// 需要自己实现
    setMockTestClient(testClient.get());

    int input = 1000;
    CheckOutPut(input);

    EXPECT_EQ(input, 1000);
}

为了跟项目业务代码做隔离,我才用编译宏隔离的方式进行,只有在编译宏UNIT_TEST生效的情况下才会进行插桩和执行用例,同时取消业务代码中多线程拉起,采用单进程方式进行测试。
在项目.pro文件中添加编译宏:

DEFINES += UNIT_TEST

5 效果展示

写完用例代码后编译调试运行,终于完成了基本用例并顺利通过:
在这里插入图片描述
如果想要执行特定的用例,可以在Qt Creator里面进行配置过滤,配置完成后即可只跑特定的用例:
在这里插入图片描述

如果你想编写一个gtest测试用例来测试分割字符串的函数,可以使用Google Test框架来实现。下面是一个示例: ```cpp #include <gtest/gtest.h> #include <vector> #include <string> // 分割字符串函数 std::vector<std::string> splitString(const std::string& str, char delimiter) { std::vector<std::string> tokens; size_t start = 0; size_t end = str.find(delimiter); while (end != std::string::npos) { tokens.push_back(str.substr(start, end - start)); start = end + 1; end = str.find(delimiter, start); } tokens.push_back(str.substr(start)); return tokens; } // 测试用例 TEST(StringSplitTest, SplitStringByComma) { std::string str = "Hello,World,How,Are,You"; std::vector<std::string> expected = {"Hello", "World", "How", "Are", "You"}; std::vector<std::string> actual = splitString(str, ','); ASSERT_EQ(expected, actual); } TEST(StringSplitTest, SplitStringBySpace) { std::string str = "Hello World How Are You"; std::vector<std::string> expected = {"Hello", "World", "How", "Are", "You"}; std::vector<std::string> actual = splitString(str, ' '); ASSERT_EQ(expected, actual); } // 运行测试 int main(int argc, char** argv) { testing::InitGoogleTest(&argc, argv); return RUN_ALL_TESTS(); } ``` 上述代码中,我们定义了一个名为`splitString`的函数,该函数用于分割字符串。然后,我们使用`TEST`宏来定义两个测试用例,分别测试通过逗号和空格分割字符串的情况。在每个测试用例中,我们调用`splitString`函数并使用`ASSERT_EQ`宏来比较预期结果和实际结果是否相等。 最后,在`main`函数中使用`testing::InitGoogleTest`函数初始化gtest框架,并调用`RUN_ALL_TESTS`函数来运行所有的测试用例编译并运行这个程序,你将能够看到测试的结果。如果所有测试用例都通过,将会输出"OK",否则将显示失败的详细信息。
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值