添加链接
link管理
链接快照平台
  • 输入网页链接,自动生成快照
  • 标签化管理网页链接
相关文章推荐
近视的充电器  ·  Pandas: source of ...·  2 天前    · 
爱喝酒的四季豆  ·  So lost·  2 天前    · 
纯真的木瓜  ·  基于 Generator 与 ...·  2 天前    · 
俊逸的圣诞树  ·  Test Page for the ...·  3 天前    · 
刚毅的皮带  ·  C#调节电脑亮度_c# ...·  2 月前    · 
刚分手的花生  ·  mysql ...·  4 月前    · 
骑白马的金针菇  ·  Migration Guide - ...·  5 月前    · 

由于工作原因,之前在CI这一块一直是依照公司流程走的,LLT这一块都是照猫画虎,对于整体框架自己没有一个完整的概念,最近有时间,研究了一下整体的逻辑框架,在此记录一下。

关于gtest,gmock和mockcpp,这里不再细讲,知道gtest,gmock是google的一套用于C/C++ LLT的框架即可,要用到mockcpp是因为gmock其实只能用于对对象函数的mocker,不能对C代码中的一般函数进行mcoker,这个在后面的代码中可以看出来。

一、准备工作

1. googletest、googlemock

下载: https://github.com/google/googletest

2. mockcpp

https://code.google.com/archive/p/mockcpp/downloads

3. 编译mockcpp

最新的2.6版本的mockcpp只需要解压然后进入根目录,然后:

cmake .  //生成Makefile

make install

这里需要把libmockcpp.a保存下来,后面会用到;

/usr/local/include/文件夹下面的所有文件也需要保存下来;

4. 编译googletest

直接在下载地根目录进行

cmke .

即可得到gmock, gtest编译结果和静态库:

保存libgtest.a和libgmock.a后面用于静态链接;

二、建立测试工程

1. 创建GtestLearn工程,这里是我们需要测试的代码,目录结构如下:

mian.c

 1 #include <stdio.h>
 2 #include "func.h"
 4 int main(int argc, char **argv) {
 5     int ret = 0;
 6     struct test_t test;
 8     ret = add(1, 2);
 9     printf("Get add result: %dn", ret);
11     test.a = 10;
12     test.b = 12;
13     ret = add_struct(&test);
14     printf("Get add struct result: %dn", ret);
16     test.p_func = NULL;
17     ret = test_struct_func(&test);
18     printf("Get test struct result: %dn", ret);
20     ret = test_stub_func();
21     printf("Get test stub func result: %dn", ret);
23     return 0;

func.h

#ifndef GTESTLEARN_FUNC_H
#define GTESTLEARN_FUNC_H
struct test_t {
    int a;
    int b;
    int (*p_func)(struct test_t *test);
int add(int a, int b);
int multi(int a, int b);
int add_struct(struct test_t *test);
int test_struct_func(struct test_t *test);
int test_stub_func();
#endif //GTESTLEARN_FUNC_H

func.c

#include <stdio.h>
#include "ex_func.h"
#include "func.h"
int add(int a, int b)
    printf("start to compute the sum of a %d and b %dn", a, b);
    return a + b;
int multi(int a, int b)
    printf("start to compute the multi of a %d and b %dn", a, b);
    return a * b;
int add_struct(struct test_t *test)
    int sum;
    int multi_v;
    printf("start to compute the sum of a %d and b %dn", test->a, test->b);
    sum = test->a + test->b;
    multi_v = multi(test->a, test->b);
    if (sum > multi_v) {
        return sum;
    return multi_v;
int test_struct_func(struct test_t *test)
    if (test->p_func == NULL) {
        printf("get null func pointern");
        return 0xFFFF;
    printf("start ro run test_struct_func with a %d b %dn", test->a, test->b);
    return test->p_func(test);
int test_stub_func()
    int ret;
    int a = 0;
    ret = ex_get_value(&a);
    if (ret == 0xFFFF) {
        printf("get extern value failed, ret %dn", ret);
        return ret;
    printf("get extern value succeed, ex value %dn", a);
    return ret;

ex_func.h

#ifndef GTESTLEARN_EX_FUNC_H
#define GTESTLEARN_EX_FUNC_H
int ex_get_value(int *a);
#endif //GTESTLEARN_EX_FUNC_H

ex_func.c

#include "ex_func.h"
int ex_get_value(int *a)
    *a = 101010;
    return 0;

CMakeLists.txt

cmake_minimum_required(VERSION 3.15)
project(GtestLearn C)

set(CMAKE_C_STANDARD 99)
set(CMAKE_C_FLAGS "${CAMKE_C_FLAGS} -fprofile-arcs -ftest-coverage")
include_directories(${CMAKE_CURRENT_SOURCE_DIR}/include/)
set(SOURCE_FILES
main.c
include/func.h
func.c
include/ex_func.h
include/ex_func.c)

add_executable(GtestLearn ${SOURCE_FILES})
target_link_libraries(GtestLearn ${LIBRARIES})

这里保证通过

cmake .

然后执行,预期正确。

2. 创建测试工程GtestLearnLLT,独立一个工程是因为在正式开发环境中,做LLT的代码往往是与业务代码分离的,更好的模拟真实使用环境。

目录结构:

这里将googlemock,googletest和mockcpp放在third_party文件夹,都是由之前下载的源码解压而来,其中:

mockcpp只需要保留3rdparty文件夹,include文件用之前编译的头文件代替(准备工作第3步);lib目录里为之前编译产生的静态库;

stubs文件夹保存的是桩函数;

googlemock和googletest新建lib目录,存放之前编译的静态库;

main.cpp

#include <stdio.h>
#include "gtest/gtest.h"
GTEST_API_ int main(int argc, char **argv) {
    printf("Running main() from gtest_main.ccn");
    testing::InitGoogleTest(&argc, argv);
    return RUN_ALL_TESTS();

gtest_ut.cpp

extern "C" {
#include <stdio.h>
#include <stdlib.h>
#include "func.h"
#include <limits.h>
#include <mockcpp/mockcpp.hpp>
#include "gmock/gmock.h"
#include "gtest/gtest.h"
// using namespace std;
using namespace testing;
class GtestUt : public testing::Test
protected:
    void SetUp() override
        std::cout << "--Gtest_Ut SetUP--" << std::endl;
    void TearDown() override
        std::cout << "--Gtest_Ut TearDown--" << std::endl;
class Mock_FOO {
public:
    MOCK_METHOD1(mock_test_struct_func, int(struct test_t *test));
Mock_FOO mocker;
int mock_test_struct_func(struct test_t *test)
    return mocker.mock_test_struct_func(test);
TEST_F(GtestUt, ut_add_01)
    int ret;
    ret = add(1, 2);
    EXPECT_EQ(3, ret);
TEST_F(GtestUt, ut_add_02)
    int ret;
    struct test_t test;
    test.a = 1;
    test.b = 1;
    MOCKER(multi)
    .expects(atMost(20))
    .will(returnValue(100));
    ret = add_struct(&test);
    EXPECT_EQ(ret, 100);
    GlobalMockObject::verify();
TEST_F(GtestUt, ut_add_03)
    int ret;
    struct test_t test;
    test.a = 10;
    test.b = 11;
    MOCKER(multi)
    .expects(atMost(20))
    .will(returnValue(20));
    ret = add_struct(&test);
    EXPECT_EQ(ret, 21);
    GlobalMockObject::verify();
TEST_F(GtestUt, ut_add_04)
    int ret;
    int a, b;
    struct test_t test;
    test.a = 10;
    test.b = 11;
    test.p_func = mock_test_struct_func;
    EXPECT_CALL(mocker, mock_test_struct_func(&test)).WillRepeatedly(Return(10));
    ret = test_struct_func(&test);
    EXPECT_EQ(ret, 10);
    GlobalMockObject::verify();
TEST_F(GtestUt, ut_add_05)
    int ret;
    int ex_value;
    ret = test_stub_func();
    EXPECT_EQ(ret, 1011);

my_stubs.c

#include <stdio.h>
#include "ex_func.h"
int ex_get_value(int *a)
    if (a == NULL) {
        printf("get null pointer %pn", a);
        return 0xFFFF;
    *a = 1011;
    printf("run stub func, get value %dn", *a);
    return *a;

CMakeLists.txt

cmake_minimum_required(VERSION 3.15)
project(GtestLearnLLT)

set(CMAKE_CXX_STANDARD 14)
set(CMAKE_CXX_FLAGS "${CAMKE_CXX_FLAGS} -std=c++11 -pthread -fprofile-arcs -ftest-coverage")
set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -DCONFIG_LLT -fprofile-arcs -ftest-coverage")
set(THIRD_PARTY_PATH ${CMAKE_CURRENT_SOURCE_DIR}/third_party)

link_directories(${THIRD_PARTY_PATH}/mockcpp/lib
${THIRD_PARTY_PATH}/googlemock/lib
${THIRD_PARTY_PATH}/googletest/lib)

#${THIRD_PARTY_PATH}/googletest/
include_directories(${THIRD_PARTY_PATH}/googletest/include/
${THIRD_PARTY_PATH}/googlemock/include/
${THIRD_PARTY_PATH}/mockcpp/include/
${THIRD_PARTY_PATH}/mockcpp/3rdparty/
${THIRD_PARTY_PATH}/googlemock/
${CMAKE_CURRENT_SOURCE_DIR}/../GtestLearn/include/
${CMAKE_CURRENT_SOURCE_DIR}/stubs/)

#${THIRD_PARTY_PATH}/googletest/src/gtest-all.cc
set(SRC_FILES
${CMAKE_CURRENT_SOURCE_DIR}/../GtestLearn/func.c
${CMAKE_CURRENT_SOURCE_DIR}/../GtestLearn/main.c
${CMAKE_CURRENT_SOURCE_DIR}/stubs/my_stubs.c
${THIRD_PARTY_PATH}/googlemock/src/gmock-all.cc
gtest_ut.cpp
main.cpp)

set(CMAKE_RUNTIME_OUTPUT_DIRECTORY "${CMAKE_CURRENT_SOURCE_DIR}/bin")

add_executable(GtestLearnLLT ${SRC_FILES})
target_link_libraries(GtestLearnLLT libmockcpp.a libgmock.a libgtest.a)

编译执行:

cmake .

执行结果:

 注意:这里还是在编译的时候编译了gmock而不是直接链接,是因为直接使用静态库,会导致mock对象泄露的问题,目前没有定位到原因:

 三、代码分析

1. 不需要打桩的普通函数LLT:

TEST_F(GtestUt, ut_add_01)
    int ret;
    ret = add(1, 2);
    EXPECT_EQ(3, ret);

这个比较简单,没有什么说的,依靠gtest框架,检查函数功能的正确性;

2. 使用mockcpp来mocker一般C函数:

TEST_F(GtestUt, ut_add_02)
    int ret;
    struct test_t test;
    test.a = 1;
    test.b = 1;
    MOCKER(multi)
    .expects(atMost(20))
    .will(returnValue(100));
    ret = add_struct(&test);
    EXPECT_EQ(ret, 100);
    GlobalMockObject::verify();

使用mockcpp提供的MOCKER来对需要验证的目标函数add_struct中的multi来进行mocker,进行打桩,常用来覆盖不同的异常分支。

3. 使用自定义桩函数

int ex_get_value(int *a)
    if (a == NULL) {
        printf("get null pointer %pn", a);
        return 0xFFFF;
    *a = 1011;
    printf("run stub func, get value %dn", *a);
    return *a;
TEST_F(GtestUt, ut_add_05)
    int ret;
    int ex_value;
    ret = test_stub_func();
    EXPECT_EQ(ret, 1011);

对test_stub_func里面调用的ex_get_value来进行打桩,来实现自己想要完成的代码逻辑;

4. 使用gmock来打桩对象类函数,本例中的对象类函数是放在结构体test_t中的:

struct test_t {
    int a;
    int b;
    int (*p_func)(struct test_t *test);

使用gmock打桩:

class Mock_FOO {
public:
    MOCK_METHOD1(mock_test_struct_func, int(struct test_t *test));
Mock_FOO mocker;
int mock_test_struct_func(struct test_t *test)
    return mocker.mock_test_struct_func(test);

用Mock_FOO类的mock_test_struct_func函数来mocker结构体test_t的成员函数p_func:

TEST_F(GtestUt, ut_add_04)
    int ret;
    int a, b;
    struct test_t test;
    test.a = 10;
    test.b = 11;
    test.p_func = mock_test_struct_func;
    EXPECT_CALL(mocker, mock_test_struct_func(&test)).WillRepeatedly(Return(10));
    ret = test_struct_func(&test);
    EXPECT_EQ(ret, 10);
    GlobalMockObject::verify();

代码中 test.p_func = mock_test_struct_func; 使用Mock_FOO的函数来替代原结构体中的成员函数,通过 EXPECT_CALL  在test_struct_func函数调用结构体成员函数p_func的时候使用Mock_FOO的 mock_test_struct_func 来代替,达到打桩的目的。

注意LLT工程的CMakeLists.txt中有一个链接libmockcpp.a的操作,这里因为之前已经将mockcpp已经编译过了,这里直接使用该静态库即可;理论上gmock和gtest也可以这么操作,但是因为但是本地没有编译通过。。。。所以就直接将原gtest和gmock需要的代码放在LLT编译的时候一起编译了。

set(SRC_FILES
        ${CMAKE_CURRENT_SOURCE_DIR}/../GtestLearn/func.c
        ${CMAKE_CURRENT_SOURCE_DIR}/stubs/my_stubs.c
        ${THIRD_PARTY_PATH}/googletest/src/gtest-all.cc
        ${THIRD_PARTY_PATH}/googlemock/src/gmock-all.cc
        gtest_ut.cpp
        main.cpp)