gtest :

google 开源的跨平台C++单元测试框架,是为在不同平台上为编写C++测试而生成的。它提供了丰富的断言、致命和非致命判断、参数化、”死亡测试”等等。

不推荐将gtest直接安装到/usr/local下,当多个项目的gtest有不同配置时,将会产生难以debug的问题。

所以官方推荐将gtest作为每个项目的子项目单独管理。

用起来和普通的库差不多,只需要设置

  1. include & 修改 main 文件
  2. 指定 头文件 源文件 目录
  3. 指定 连接的库

安装方式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_EQASSERT_FALSEASSERT_GEASSERT_GTASSERT_LEASSERT_LTASSERT_NEASSERT_TRUEEXPECT_FALSEEXPECT_TRUEFAILSUCCEEDTESTTEST_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();
}

运行结果:

gtest/gmock 安装和使用_c++

分成两个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); }

结果:

gtest/gmock 安装和使用_数据库_02

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();
}

输出:


gtest/gmock 安装和使用_c++_03

其他

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.

Gmock

Gmock是google开发的一套辅助测试的工具,它往往和GTest结合在一起使用。主要用于模拟其他模块约定的接口进行自测。即该接口形式已经定义,但实现并未给出,我们用Gmock模拟其内部实现,即设定方法运行形式,来进行测试。其作用就类似白盒测试中的打桩的概念。

适用于:

  1. 测试某功能需要调用硬件或者其他服务,太过于复杂,例如测试对某函数对数据库的操作时,不需要真正调用数据库或者为了测试而搭建数据库,gmock可以将数据库接口打桩,模拟数据库的返回。
  2. A模块调用B模块,目前测试A模块,但是B模块还没有实现,可以用gmock去B模块打桩,完成A模块对B模块的调用和测试。

实现需要新写一个类继承于基类,基类包括将要测试的函数,包括纯虚函数等。

在类内我们定义测试接口的方法:

MOCK_METHODx(func_name,return_type(args...) );

x和函数的参数个数有关,func_name为函数名,return_type为函数func_name返回类型,args为参数表。

例如对于函数:

bool strcmp_my(char * str1, char *str2);

对应测试方法声明为:

MOCK_METHOD2(strcmp_my,bool(char * str1, char *str2));

对于函数

std::vector<int> makeData();

MOCK_METHOD0(makeData,std::vector<int>());

构建类和方法后,我们可以通过调用

EXPECT_CALL(test_obj, strcmp_my(_,_))

//       .With(...)

//       .Times(...)

//       .InSequence(...)

//       .After(...)

//       .WillOnce(...)

//       .WillRepeatedly(...)

//       .RetiresOnSaturation();

来期待这个方法按照什么逻辑去执行。参数表的_代表通配符,可以用任意参数代替进行控制,例如输入某字符串就会直接失败等等。test_obj为刚刚构建类的一个对象。

_可替换为匹配器,单值匹配可以直接填入该值,详细的可调用自带匹配器,或手动编写。

方法

释义

With 指定多个参数的匹配方式
Times 设定方法调用的次数
InSequence 指定函数执行的顺序
After 指定某个方法只能在另一个方法之后执行
WillOnce 执行一次方法时,将执行其参数的方法。一般我们使用Return方法,用于指定一次调用的输出。
WillRepeatedly 表示一直调用一个方法时,将执行其参数的方法。
RetiresOnSaturation 保证期待调用不会被相同的函数的期待所覆盖。

return方法,用于模拟父类函数的返回值:

testing::Return(true)

testing::Return(false)

testing::Return("123")

testing::Return(1)

实例:只有第一次执行返回true:

//第一次执行返回true,之后都返回false

EXPECT_CALL(test_obj, strcmp_fun(testing::_,testing::_)).WillOnce(testing::Return(true));

/*

相当于:

int count = 0;

bool strcmp_fun(string str1,string str2){

if(count==0){

    count++;

    return true;}

else{

    return false;

}*/

//一直返回false

EXPECT_CALL(test_obj, makeData()).WillRepeatedly(testing::Return(false));

/*

相当于:

bool strcmp_fun(string str1,string str2){

    return false;

}*/

实例2:

//传入123,就返回false

EXPECT_CALL(test_obj, testfunc(123)).WillOnce(testing::Return(false));

//传入123,就返回true

EXPECT_CALL(test_obj, testfunc(0)).WillOnce(testing::Return(false));

实例3:

该函数调用前两次返回true,之后为false:

EXPECT_CALL(test_obj, makeData()).WillOnce(testing::Return(true)).WillOnce(testing::Return(true)).WillOnce(testing::Return(false));

最后,举个荔枝:

#include "gmock/gmock.h"

#include "gtest/gtest.h"

using namespace std;

using ::testing::Matcher;

class User {

public:

    User() {};

    ~User() {};

public:

    // 登录

    virtual bool Login(const std::string& username, const std::string& password) = 0;

    // 支付

    virtual bool Pay(int money) = 0;

    // 是否登录

    virtual bool Online() = 0;

};

class TestUser : public User {

public:

    MOCK_METHOD2(Login, bool(const std::string&, const std::string&));

    MOCK_METHOD1(Pay, bool(int));

    MOCK_METHOD0(Online, bool());

};

class Biz {

public:

    void SetUser(User* user) {

        _user = user;

    }

    std::string pay(const std::string& username, const std::string& password, int money) {

        std::string ret;

        if (!_user) {

            ret = " pointer is null.";

            return ret;

        }

        if (!_user->Online()) {

            ret = " logout status.";

            // 尚未登录,要求登录

            if (!_user->Login(username, password)) {

                // 登录失败

                ret += " login error.";

                return ret;

            else {

                // 登录成功

                ret += " login success.";

            }

        else {

            // 已登录

            ret = " login.status";

        }

        if (!_user->Pay(money)) {

            ret += " pay error.";

        else {

            ret += " pay success.";

        }

        return ret;

    }

private:

    User* _user;

};

MATCHER_P(IsPos,a,"") {

    if (arg > 0) {

        return true;

    }

    else{

        return false;

    }

}

MATCHER_P(IsNEG,a,"") {

    if (arg <= 0) {

        return true;

    }

    else{

        return false;

    }

}

int main(int argc, char *argv[]) {

        TestUser test_user;

        EXPECT_CALL(test_user, Online()).WillRepeatedly(testing::Return(true));

        EXPECT_CALL(test_user, Login(testing::_,testing::_)).WillRepeatedly(testing::Return(false));

        EXPECT_CALL(test_user, Pay(Matcher<int>(IsPos(testing::_)))).WillRepeatedly(testing::Return(true));

        EXPECT_CALL(test_user, Pay(Matcher<int>(IsNEG(testing::_)))).WillRepeatedly(testing::Return(false));

        Biz biz;

        biz.SetUser(&test_user);

        std::string admin_ret = biz.pay("user""", 1);

        cout << admin_ret<<endl;

        admin_ret = biz.pay("user""", 0);

        cout << admin_ret<<endl;

}

参考文献:

前端 google 开源测试框架 Google test(gtest) 入门

googletest/README.md

gtest测试框架

Google C++单元测试框架---Gtest框架简介(译文)

gtest的介绍和使用

Gtest:死亡测试

玩转Google开源C++单元测试框架Google Test系列(gtest)之四 - 参数化

玩转Google开源C++单元测试框架Google Test系列(gtest)之八 - 打造自己的单元测试框架

Google Mock启蒙篇

Google Mock(Gmock)简单使用和源码分析——简单使用

gmock学习02---编写自己的Matcher与如何让编译器识别被mock的重载函数