目录
0 背景
之前写代码一直有习惯于搭建LLT框架,作为测试防护网能够大大提升代码健壮性,有效避免修改引入,另一方面测试用例作为项目重要资产也应该有个地方可以归档,写入LLT用例里面还可以通过git上库。
当前项目中只有业务应用代码,还未有对应用例,为了后续开发以及测试方便,抽了点时间研究在windows下编译GTest并执行。
1 环境
本地项目编译/运行的相关环境如下所示:
编号 | 名称 | 版本 |
---|---|---|
1 | 操作系统 | Windows 11 |
2 | 编译环境 | Qt 5.8 |
3 | 编程语言 | C++14 |
4 | 编译器版本 | VS2015 MSVC 19.0 |
5 | GTest版本 | 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里面进行配置过滤,配置完成后即可只跑特定的用例: