google 开源的跨平台C++单元测试框架,是为在不同平台上为编写C++测试而生成的。它提供了丰富的断言、致命和非致命判断、参数化、”死亡测试”等等。
不推荐将gtest直接安装到/usr/local下,当多个项目的gtest有不同配置时,将会产生难以debug的问题。
所以官方推荐将gtest作为每个项目的子项目单独管理。
用起来和普通的库差不多,只需要设置
- include & 修改 main 文件
- 指定 头文件 源文件 目录
- 指定 连接的库
安装方式1:作为独立项目
git clone https://github.com/google/googletest.git -b release-1.11.0
cd googletest # Main directory of the cloned repository.
mkdir build # Create a directory to hold the build output.
cd build
cmake .. # Generate native build scripts for GoogleTest.
make
sudo make install
其他项目:
find_package(GTest CONFIG REQUIRED)
安装方式2:作为已有项目的子模块
使用 CMake 下载 GoogleTest 作为构建配置步骤的一部分。
way1:在主项目下新建CMakeLists.txt.in:
cmake_minimum_required(VERSION 2.8.2)
project(googletest-download NONE)
include(ExternalProject)
ExternalProject_Add(googletest
GIT_REPOSITORY https://github.com/google/googletest.git
GIT_TAG master
SOURCE_DIR "${CMAKE_BINARY_DIR}/googletest-src"
BINARY_DIR "${CMAKE_BINARY_DIR}/googletest-build"
CONFIGURE_COMMAND ""
BUILD_COMMAND ""
INSTALL_COMMAND ""
TEST_COMMAND ""
)
CMakeLists.txt新加:
configure_file(CMakeLists.txt.in googletest-download/CMakeLists.txt)
execute_process(COMMAND ${CMAKE_COMMAND} -G "${CMAKE_GENERATOR}" .
RESULT_VARIABLE result
WORKING_DIRECTORY ${CMAKE_BINARY_DIR}/googletest-download )
if(result)
message(FATAL_ERROR "CMake step for googletest failed: ${result}")
endif()
execute_process(COMMAND ${CMAKE_COMMAND} --build .
RESULT_VARIABLE result
WORKING_DIRECTORY ${CMAKE_BINARY_DIR}/googletest-download )
if(result)
message(FATAL_ERROR "Build step for googletest failed: ${result}")
endif()
# Add googletest directly to our build. This defines
# the gtest and gtest_main targets.
add_subdirectory(${CMAKE_BINARY_DIR}/googletest-src
${CMAKE_BINARY_DIR}/googletest-build
EXCLUDE_FROM_ALL)
# The gtest/gtest_main targets carry header search path
# dependencies automatically when using CMake 2.8.11 or
# later. Otherwise we have to add them here ourselves.
if (CMAKE_VERSION VERSION_LESS 2.8.11)
include_directories("${gtest_SOURCE_DIR}/include")
endif()
# Now simply link against gtest or gtest_main as mytest
aux_source_directory(. D_S)
add_executable(mytest ${D_S})
target_link_libraries(mytest gtest_main)
#add_test(mytest example_test COMMAND mytest)
way 2:(官方)
cmake版本最小为3.14!
CMakeLists.txt新加:
include(FetchContent)
FetchContent_Declare(
googletest
# Specify the commit you depend on and update it regularly.
URL https://github.com/google/googletest/archive/609281088cfefc76f9d0ce82e1ff6c30cc3591e5.zip
)
)
FetchContent_MakeAvailable(googletest)
# Now simply link against gtest or gtest_main as needed. Eg
add_executable(sample_gtest sample/src/sample_gtest.cpp)
target_link_libraries(sample_gtest gtest_main) #your .cpp name
add_test(NAME example_test COMMAND sample_gtest)
使用:
主要宏:
ASSERT_EQ
, ASSERT_FALSE
, ASSERT_GE
, ASSERT_GT
, ASSERT_LE
, ASSERT_LT
, ASSERT_NE
, ASSERT_TRUE
, EXPECT_FALSE
, EXPECT_TRUE
, FAIL
, SUCCEED
, TEST
, TEST_F
TEST(testsuiteName, testcaseName)
TEST宏是gtest最基本的使用单元,一个testsuitName可以包含多个不同的testcaseName。每个testsuiteName和testcaseName构成的TEST宏内部就是该测试样例的具体实现。
testcaseName和testsuiteName内部会经过转换,所以最好不要含有下划线,例如 _case1,case1_,case_1等,以避免不必要的麻烦。
例如想判断add()函数的输出是否和理想一致,通过写了两个case的测试样例进行测试,并使用EXPECT_EQ进行判断:
#include "gmock/gmock.h"
#include "gtest/gtest.h"
int add(int a, int b) { return a + b; }
TEST(testsuite1, case1) { EXPECT_EQ(add(1, 1), 3); }
TEST(testsuite1, case2) { EXPECT_EQ(add(1, 2), 3); }
int main(int argc, char *argv[]) {
::testing::InitGoogleTest(&argc, argv);
return RUN_ALL_TESTS();
}
运行结果:
分成两个testsuite:
TEST(testsuite1, case1) { EXPECT_EQ(add(1, 1), 3); }
TEST(testsuite1, case2) { EXPECT_EQ(add(1, 2), 3); }
TEST(testsuite2, case1) { EXPECT_EQ(add(2, 2), 4); }
TEST(testsuite2, case2) { EXPECT_EQ(add(2, 3), 5); }
结果:
ASSERT_*/EXCEPT_*
ASSERT_* |
EXCEPT_* |
para(s) |
verifies |
---|---|---|---|
ASSERT_EQ |
EXCEPT_EQ | val1,val2 | val1 == val2 |
ASSERT_NE |
EXCEPT_NE | val1,val2 | val1 != val2 |
ASSERT_LT |
EXCEPT_LT | val1,val2 | val1 < val2 |
ASSERT_LE |
EXCEPT_LE | val1,val2 | val1 <= val2 |
ASSERT_GT |
EXCEPT_GT | val1,val2 | val1 > val2 |
ASSERT_GE |
EXCEPT_GE | val1,val2 | val1 >= val2 |
ASSERT_TRUE |
EXCEPT_TRUE | exp | (exp) == true |
ASSERT_FALSE |
EXCEPT_FALSE | exp | (exp) == false |
以上val1仅支持数字、bool、char的判断,以及判断两个指针是否指向同一地址。其他字符串、vector等需要其他宏,没有可能要手动写。
字符串可以用ASSERT_STREQ、STRNE、STRCASREQ、STRCASENE(CASE:不区分大小学)
浮点数最好用ASSERT_DOUBLE_EQ、ASSERT_FLOAT_EQ、ASSERT_NEAR
指针判断:
#include "gmock/gmock.h"
#include "gtest/gtest.h"
TEST(testsuite1, case1) {
//two ptr point to the same address
int a =1;
int *p1 = &(a);
int *p2 = &(a);
EXPECT_EQ(p1, p2);
}
TEST(testsuite1, case2) {
//two ptr point to the diff address, but the value is the same
int a = 1;
int b = 1;
int *p1 = &(a);
int *p2 = &(b);
EXPECT_EQ(p1, p2);
}
TEST(testsuite1, case3) {
//two ptr point to the diff address, but the value is not the same
int a = 1;
int b = 2;
int *p1 = &(a);
int *p2 = &(b);
EXPECT_EQ(p1, p2);
}
int main(int argc, char *argv[]) {
::testing::InitGoogleTest(&argc, argv);
return RUN_ALL_TESTS();
}
输出:
其他
FAIL() 只要在一个TEST中出现一次,代表该样例失败,不管之后发生什么。
SUCCESS() 只代表在TEST中某一部分成功,但是可能会被后续影响。
TEST(testsuite1, case1) {
SUCCEED();
}
TEST(testsuite1, case2) {
SUCCEED();
FAIL();
SUCCEED();
}
以上结果分别为成功、失败。
参数化:
class IsPosParamTest : public::testing::TestWithParam<int>//<T>代表参数类型 IsPosParamTest为测试套件名
{
};
INSTANTIATE_TEST_CASE_P(cases, IsPosParamTest, testing::Values(3, 5, -1, 23, 17));//分别为样例名,测试套件名,数据
//范围数据 :testing::Range(0,99,2) 生成0-100,的偶数(不包括100)
//从stl取值:testing::ValuesIn(vector1) ValuesIn(vector2.begin(),vector2.begin()+5)
//排列组合:testing::Combine(g1,g2,...) g*为参数生成器,分别从g1、g2等取值进行排列组合。即套娃
int isPos(int a) { return a >=0 ; }
TEST_P(IsPosParamTest, cases) { int n = GetParam(); EXPECT_TRUE(isPos(n)); }
int main(int argc, char *argv[]) {
::testing::InitGoogleTest(&argc, argv);
return RUN_ALL_TESTS();
}
case:利用Combine生成vector,并进行判断:
#include "mdata_hub.h"
#include "gtest/gtest.h"
#include <tuple>
void ASSERT_VECTOR_EQ(std::vector<double_t> vec1, std::vector<double_t> vec2) {
ASSERT_EQ(vec1.size(), vec2.size());
for (size_t i{0LU}; i < vec1.size(); i++) { ASSERT_DOUBLE_EQ(vec1[i], vec2[i]); }
}
class MdataHubDropTesting : public ::testing::TestWithParam<std::tuple<double_t,double_t> > {
protected:
virtual void SetUp()
{
vec.push_back( std::get<0>(GetParam()));
vec.push_back( std::get<1>(GetParam()));
}
virtual void TearDown()
{
}
std::vector<double_t> vec;
};
INSTANTIATE_TEST_CASE_P(dropcases, MdataHubDropTesting, testing::Combine(testing::Values(2,3),testing::Values(2,3)));
TEST_P(MdataHubDropTesting, dropcases) {
std::vector<double_t> ans{2.0,2.0};
ASSERT_VECTOR_EQ(vec,ans);
}
int main(int argc, char *argv[]) {
::testing::InitGoogleTest(&argc, argv);
return RUN_ALL_TESTS();
}
多测试套件参数化实例:
class MdataHubDropTesting : public ::testing::TestWithParam<V1> {};
class MdataHubShuffleTesting : public ::testing::TestWithParam<V1> {};
INSTANTIATE_TEST_CASE_P(dropcases, MdataHubDropTesting, testing::ValuesIn(out_files[0]));
INSTANTIATE_TEST_CASE_P(shufflecases, MdataHubShuffleTesting, testing::ValuesIn(out_files[1]));
TEST_P(MdataHubDropTesting, dropcases) {
auto vec = GetParam();
ASSERT_VECTOR_EQ(vec, gt_files[0][num1++]);
}
TEST_P(MdataHubShuffleTesting, shufflecases) {
auto vec = GetParam();
ASSERT_VECTOR_EQ(vec, gt_files[1][num2++]);
}
注意:TEST_P似乎是并行处理,即:
class MdataHubDropTesting : public ::testing::TestWithParam<V1> {
public:
std::size_t num{0LU};
};
INSTANTIATE_TEST_CASE_P(dropcases, MdataHubDropTesting, testing::ValuesIn(out_files[0]));
TEST_P(MdataHubDropTesting, dropcases) {
auto vec = GetParam();
ASSERT_VECTOR_EQ(vec, gt_files[0][num++]);
std::cout<<num<<std::endl;
}
输出值一直为1.
GmockGmock是google开发的一套辅助测试的工具,它往往和GTest结合在一起使用。主要用于模拟其他模块约定的接口进行自测。即该接口形式已经定义,但实现并未给出,我们用Gmock模拟其内部实现,即设定方法运行形式,来进行测试。其作用就类似白盒测试中的打桩的概念。
适用于:
- 测试某功能需要调用硬件或者其他服务,太过于复杂,例如测试对某函数对数据库的操作时,不需要真正调用数据库或者为了测试而搭建数据库,gmock可以将数据库接口打桩,模拟数据库的返回。
- A模块调用B模块,目前测试A模块,但是B模块还没有实现,可以用gmock去B模块打桩,完成A模块对B模块的调用和测试。
实现需要新写一个类继承于基类,基类包括将要测试的函数,包括纯虚函数等。
在类内我们定义测试接口的方法:
|
x和函数的参数个数有关,func_name为函数名,return_type为函数func_name返回类型,args为参数表。
例如对于函数:
|
对应测试方法声明为:
|
对于函数
|
|
构建类和方法后,我们可以通过调用
|
来期待这个方法按照什么逻辑去执行。参数表的_代表通配符,可以用任意参数代替进行控制,例如输入某字符串就会直接失败等等。test_obj为刚刚构建类的一个对象。
_可替换为匹配器,单值匹配可以直接填入该值,详细的可调用自带匹配器,或手动编写。
方法 |
释义 |
---|---|
With | 指定多个参数的匹配方式 |
Times | 设定方法调用的次数 |
InSequence | 指定函数执行的顺序 |
After | 指定某个方法只能在另一个方法之后执行 |
WillOnce | 执行一次方法时,将执行其参数的方法。一般我们使用Return方法,用于指定一次调用的输出。 |
WillRepeatedly | 表示一直调用一个方法时,将执行其参数的方法。 |
RetiresOnSaturation | 保证期待调用不会被相同的函数的期待所覆盖。 |
return方法,用于模拟父类函数的返回值:
|
实例:只有第一次执行返回true:
|
实例2:
|
实例3:
该函数调用前两次返回true,之后为false:
|
最后,举个荔枝:
|
前端 google 开源测试框架 Google test(gtest) 入门
Google C++单元测试框架---Gtest框架简介(译文)
玩转Google开源C++单元测试框架Google Test系列(gtest)之四 - 参数化
玩转Google开源C++单元测试框架Google Test系列(gtest)之八 - 打造自己的单元测试框架
Google Mock(Gmock)简单使用和源码分析——简单使用
gmock学习02---编写自己的Matcher与如何让编译器识别被mock的重载函数