• cpp-stub使用教程


    一、简介

    配合Gtest使用,因为Gtest的所有接口都是基于虚函数的;
    此外,由于一些依赖接口不容易Stub或者Mock(或者写Stub test过于麻烦),因此在Gtest的基础上使用cpp-stub进行单元测试用例编写。

    二、环境搭建

    以ubuntu为例:

    apt-get update -y
    apt-get install -y libgtest-dev
    apt-get install -y libgmock-dev
    
    • 1
    • 2
    • 3

    一个gtest sample:

    
    #include 
    #include 
    
    int Foo(int a, int b) { return a + b; }
    
    TEST(FooTest, FooAddTest) {
      EXPECT_EQ(2, Foo(1, 1));
      EXPECT_EQ(6, Foo(2, 4));
    }
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11

    编译:

    root@65147725a2cb:/opt/test_gtest# g++ test.cc -l gtest -lgmock -lgtest_main -lpthread
    
    • 1

    Stub API 源码地址
    将stub源码clone到本地一个位置

    git clone git@github.com:coolxv/cpp-stub.git
    
    • 1

    cpp-stub-ext
    cpp-stub-ext克隆到本地(Optional)

    git clone git@gitee.com:master-roc/cpp-stub-ext.git
    
    • 1

    结合方法:

    (1)将cpp-stub-ext与cpp-stub一起导入工
    (2)修改cpp-stub的stub.h文件,将Stub类的私用函数和变量修改为保护型。

    三、Stub cpp使用

    1.使用教程

    链接:doc_zh.md

    例子大多来源于:link

    1.1 不能打桩

    • 不能对 exit 函数打桩,编译器做优化了
    • 不能对纯虚函数打桩, 纯虚函数没有地址
    • 不能对 lambda 函数打桩, lambda 函数获取不到地址
    • 不能对静态函数打桩, 静态函数地址不可见.(但可以尝试使用 addr_any.h 接口获取地址)

    1.2 单元测试编译选项, linux g++可用的

    • -fno-access-control,private->public
    • -fno-inline,禁用内联函数
    • -Wno-pmf-conversions,允许成员函数转为普通函数指针
    • -Wl,–allow-multiple-definition,允许重复定义
    • -no-pie -fno-stack-protector,禁用栈保护(Stack Protector)功能
    • -fprofile-arcs,用于生成代码覆盖率信息。
    • -ftest-coverage,编译和链接时启用代码覆盖率分析。

    1.3 代码覆盖率, linux g++使用方法

    lcov -d build/ -z
    lcov -d build/ -b ../../src1 --no-external -rc lcov_branch_coverage=1 -t ut -c -o ut_1.info
    lcov -d build/ -b ../../src2 --no-external -rc lcov_branch_coverage=1 -t ut -c -o ut_2.info
    lcov -a ut_1.info -a ut_2.info -o ut.info
    genhtml -o report/ --prefix=`pwd` --branch-coverage --function-coverage ut.info
    
    • 1
    • 2
    • 3
    • 4
    • 5

    1.4普通函数打桩(非static)

    // for linux and windows
    #include "stub.h"
    #include 
    
    using namespace std;
    int foo(int a) {
      cout << "I am foo" << endl;
      return 0;
    }
    
    int foo_stub(int a) {
      cout << "I am foo_stub" << endl;
      return 0;
    }
    
    int main() {
      Stub stub;
      stub.set(foo, foo_stub);
      foo(1);
      return 0;
    }
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22

    编译以及运行

    root@65147725a2cb:/opt/test_gtest# g++ test1.cc -I ../cpp-stub/src/
    root@65147725a2cb:/opt/test_gtest# ./a.out 
    I am foo_stub
    
    • 1
    • 2
    • 3

    1.5 对象成员函数打桩

    eg1

    // for linux,__cdecl
    #include "stub.h"
    #include 
    using namespace std;
    class A {
      int i;
    
    public:
      int foo(int a) {
        cout << "I am A_foo" << endl;
        return 0;
      }
    };
    
    int foo_stub(void *obj, int a) {
      A *o = (A *)obj;
      cout << "I am foo_stub" << endl;
      return 0;
    }
    
    int main() {
      Stub stub;
      stub.set(ADDR(A, foo), foo_stub);
      A a;
      a.foo(1);
      return 0;
    }
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28

    编译以及运行

    root@65147725a2cb:/opt/test_gtest# g++ test1.cc -I ../cpp-stub/src/
    root@65147725a2cb:/opt/test_gtest# ./a.out 
    I am foo_stub
    
    • 1
    • 2
    • 3

    eg2

    // for windows,__thiscall
    #include "stub.h"
    #include 
    using namespace std;
    class A {
      int i;
    
    public:
      int foo(int a) {
        cout << "I am A_foo" << endl;
        return 0;
      }
    };
    
    class B {
    public:
      int foo_stub(int a) {
        cout << "I am foo_stub" << endl;
        return 0;
      }
    };
    
    int main() {
      Stub stub;
      stub.set(ADDR(A, foo), ADDR(B, foo_stub));
      A a;
      a.foo(1);
      return 0;
    }
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30

    编译以及运行

    root@65147725a2cb:/opt/test_gtest# g++ test1.cc -I ../cpp-stub/src/
    root@65147725a2cb:/opt/test_gtest# ./a.out 
    I am foo_stub
    
    • 1
    • 2
    • 3

    1.6 静态成员函数打桩

    // for linux and windows
    #include "stub.h"
    #include 
    using namespace std;
    class A {
      int i;
    
    public:
      static int foo(int a) {
        cout << "I am A_foo" << endl;
        return 0;
      }
    };
    
    int foo_stub(int a) {
      cout << "I am foo_stub" << endl;
      return 0;
    }
    
    int main() {
      Stub stub;
      stub.set(ADDR(A, foo), foo_stub);
    
      A::foo(1);
      return 0;
    }
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27

    编译以及运行

    root@65147725a2cb:/opt/test_gtest# g++ test1.cc -I ../cpp-stub/src/
    root@65147725a2cb:/opt/test_gtest# ./a.out 
    I am foo_stub
    
    • 1
    • 2
    • 3

    1.7模板函数打桩

    // for linux,__cdecl
    #include "stub.h"
    #include 
    using namespace std;
    class A {
    public:
      template <typename T> int foo(T a) {
        cout << "I am A_foo" << endl;
        return 0;
      }
    };
    
    int foo_stub(void *obj, int x) {
      A *o = (A *)obj;
      cout << "I am foo_stub" << endl;
      return 0;
    }
    
    int main() {
      Stub stub;
      stub.set((int(A::*)(int))ADDR(A, foo), foo_stub);
      A a;
      a.foo(5);
      return 0;
    }
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26

    编译及运行

    root@65147725a2cb:/opt/test_gtest# g++ test1.cc -I ../cpp-stub/src/
    root@65147725a2cb:/opt/test_gtest# ./a.out 
    I am foo_stub
    
    • 1
    • 2
    • 3
    // for windows,__thiscall
    #include "stub.h"
    #include 
    using namespace std;
    class A {
    public:
      template <typename T> int foo(T a) {
        cout << "I am A_foo" << endl;
        return 0;
      }
    };
    
    class B {
    public:
      int foo_stub(int a) {
        cout << "I am foo_stub" << endl;
        return 0;
      }
    };
    
    int main() {
      Stub stub;
      stub.set((int(A::*)(int))ADDR(A, foo), ADDR(B, foo_stub));
      A a;
      a.foo(5);
      return 0;
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27

    编译及运行

    root@65147725a2cb:/opt/test_gtest# g++ test1.cc -I ../cpp-stub/src/
    root@65147725a2cb:/opt/test_gtest# ./a.out 
    I am foo_stub
    
    • 1
    • 2
    • 3

    1.8重载函数打桩

    // for linux,__cdecl
    #include "stub.h"
    #include 
    using namespace std;
    class A {
      int i;
    
    public:
      int foo(int a) {
        cout << "I am A_foo_int" << endl;
        return 0;
      }
      int foo(double a) {
        cout << "I am A_foo-double" << endl;
        return 0;
      }
    };
    
    int foo_stub_int(void *obj, int a) {
      A *o = (A *)obj;
      cout << "I am foo_stub_int" << a << endl;
      return 0;
    }
    int foo_stub_double(void *obj, double a) {
      A *o = (A *)obj;
      cout << "I am foo_stub_double" << a << endl;
      return 0;
    }
    
    int main() {
      Stub stub;
      stub.set((int(A::*)(int))ADDR(A, foo), foo_stub_int);
      stub.set((int(A::*)(double))ADDR(A, foo), foo_stub_double);
      A a;
      a.foo(5);
      a.foo(1.1);
      return 0;
    }
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39

    编译及运行

    root@65147725a2cb:/opt/test_gtest# g++ test1.cc -I ../cpp-stub/src/
    root@65147725a2cb:/opt/test_gtest# 
    root@65147725a2cb:/opt/test_gtest# 
    root@65147725a2cb:/opt/test_gtest# ./a.out 
    I am foo_stub_int5
    I am foo_stub_double1.1
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6

    1.9 虚函数打桩

    // for linux
    #include "stub.h"
    #include 
    using namespace std;
    class A {
    public:
      virtual int foo(int a) {
        cout << "I am A_foo" << endl;
        return 0;
      }
    };
    
    int foo_stub(void *obj, int a) {
      A *o = (A *)obj;
      cout << "I am foo_stub" << endl;
      return 0;
    }
    
    int main() {
      typedef int (*fptr)(A *, int);
    
      using virtual_function_type = int (*)(A *, int);
    
      virtual_function_type A_foo =
          (virtual_function_type)(&A::foo); // 获取虚函数地址
      Stub stub;
      stub.set(A_foo, foo_stub);
      A a;
      a.foo(1);
      return 0;
    }
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32

    编译及运行

    root@65147725a2cb:/opt/test_gtest# g++ test1.cc -I ../cpp-stub/src/
    test1.cc: In function 'int main()':
    test1.cc:25:38: warning: converting from 'int (A::*)(int)' to 'virtual_function_type' {aka 'int (*)(A*, int)'} [-Wpmf-conversions]
       25 |       (virtual_function_type)(&A::foo); // 获取虚函数地址
          |                                      ^
    root@65147725a2cb:/opt/test_gtest# ./a.out 
    I am foo_stub
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7

    重载的虚函数

    //for linux gcc
    #include
    #include "stub.h"
    using namespace std;
    class A{
        int i;
    public:
        virtual int foo(int a){
            cout<<"I am A_foo"<<endl;
            return 0;
        }
        virtual int foo(double a){
            cout<<"I am A_foo"<<endl;
            return 0;
        }
    };
    
    int foo_stub(void* obj, int a)
    {
        A* o= (A*)obj;
        cout<<"I am foo_stub"<<endl;
        return 0;
    }
    
    
    int main()
    {
        typedef int (*fptr)(A*,int);
        fptr A_foo = (fptr)((int(A::*)(int))&A::foo);
        Stub stub;
        stub.set(A_foo, foo_stub);
        A a;
        a.foo(1);
        return 0;
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35

    1.10 内联函数打桩

    //for linux
    //添加-fno-inline编译选项,禁止内联,能获取到函数地址,打桩参考上面。

    1.11 第三方库私有成员函数打桩

    // for linux
    // 被测代码添加-fno-access-private编译选项,禁用访问权限控制,成员函数都为公有的
    // 无源码的动态库或静态库无法自己编译,需要特殊技巧获取函数地址
    
    #include "addr_pri.h"
    #include "stub.h"
    #include 
    using namespace std;
    class A {
      int a;
      int foo(int x) {
        cout << "I am A_foo " << a << endl;
        return 0;
      }
      static int b;
      static int bar(int x) {
        cout << "I am A_bar " << b << endl;
        return 0;
      }
    };
    
    ACCESS_PRIVATE_FIELD(A, int, a);
    ACCESS_PRIVATE_FUN(A, int(int), foo);
    ACCESS_PRIVATE_STATIC_FIELD(A, int, b);
    ACCESS_PRIVATE_STATIC_FUN(A, int(int), bar);
    
    int foo_stub(void *obj, int x) {
      A *o = (A *)obj;
      cout << "I am foo_stub" << endl;
      return 0;
    }
    int bar_stub(int x) {
      cout << "I am bar_stub" << endl;
      return 0;
    }
    int main() {
      A a;
    
      auto &A_a = access_private_field::Aa(a);
      auto &A_b = access_private_static_field::A::Ab();
      A_a = 1;
      A_b = 10;
    
      call_private_fun::Afoo(a, 1);
      call_private_static_fun::A::Abar(1);
    
      auto A_foo = get_private_fun::Afoo();
      auto A_bar = get_private_static_fun::A::Abar();
    
      Stub stub;
      stub.set(A_foo, foo_stub);
      stub.set(A_bar, bar_stub);
    
      call_private_fun::Afoo(a, 1);
      call_private_static_fun::A::Abar(1);
      return 0;
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51
    • 52
    • 53
    • 54
    • 55
    • 56
    • 57

    编译及运行

    root@65147725a2cb:/opt/test_gtest# g++ test1.cc -I ../cpp-stub/src/ 
    root@65147725a2cb:/opt/test_gtest# ./a.out 
    I am A_foo 1
    I am A_bar 10
    I am foo_stub
    I am bar_stub
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6

    1.12可变惨函数打桩

    #include
    #include 
    #include "stub.h"
    using namespace std;
    
    double average(int num, ...)
    {
     
        va_list valist;
        double sum = 0.0;
        int i;
     
        va_start(valist, num);
     
        for (i = 0; i < num; i++)
        {
           sum += va_arg(valist, int);
        }
        va_end(valist);
        cout<<"I am foo"<<endl;
        return sum/num;
    }
    
    double average_stub(int num, ...)
    {   
        va_list valist;
        double sum = 0.0;
        int i;
     
        va_start(valist, num);
     
        for (i = 0; i < num; i++)
        {
           sum += va_arg(valist, int);
        }
        va_end(valist);
        cout<<"I am foo_stub"<<endl;
        return  sum/num;
    }
    
    int main()
    {
        cout << "Average of 2, 3, 4, 5 = " << average(4, 2,3,4,5) << endl;
        Stub stub;
        stub.set(average, average_stub);
        cout << "Average of 2, 3, 4, 5 = " << average(4, 2,3,4,5) << endl;
    
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48

    1.13 仿函数打桩

    #include
    #include "stub.h"
    using namespace std;
    
    
    class Foo
    {
    public:
        void operator() (int a)
        {
            cout<<"I am foo"<<endl;
        }
    };
    
    int foo_stub(void* obj, int a)
    {   
        Foo* o= (Foo*)obj;
        cout<<"I am foo_stub"<<endl;
        return 0;
    }
    
    int main()
    {
        Stub stub;
        stub.set(ADDR(Foo,operator()), foo_stub);
        Foo foo;
        foo(1);
        return 0;
    }
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30

    1.14 C语言标准库函数打桩

    #include "stub.h"
    #include 
    #include 
    using namespace std;
    int foo(int a) {
      puts("I am foo\n");
      return 0;
    }
    
    int printf_stub(const char *format, ...) {
      cout << "I am printf_stub" << endl;
      return 0;
    }
    
    int main() {
      Stub stub;
      stub.set(puts, printf_stub);
      foo(1);
      return 0;
    }
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21

    1.15 默认析构函数打桩(难)

    
    #include "stub.h"
    #include 
    using namespace std;
    
    template <class T> void *get_dtor_addr(bool start = true) {
      // the start vairable must be true, or the compiler will optimize out.
      if (start)
        goto Start;
      // This line of code will not be executed.
      // The purpose of the code is to allow the compiler to generate the assembly
      // code that calls the constructor.
      {
        T();
      Call_dtor:;
        ;
      }
    
    Start:
      // The address of the line of code T() obtained by assembly
      char *p =
          (char
               *)&&Call_dtor; // https://gcc.gnu.org/onlinedocs/gcc/Labels-as-Values.html
      // CALL rel32
      void *ret = 0;
      char pos;
      char call = 0xe8;
      do {
        pos = *p;
        if (pos == call) {
          ret = p + 5 + (*(int *)(p + 1));
        }
    
      } while (!ret && (--p));
    
      return ret;
    }
    
    class A {
    public:
      A() { cout << "I am A_constructor" << endl; }
      ~A() {
        { cout << "I am A_dtor" << endl; }
      }
    };
    
    class B {
    public:
      B() { cout << "I am B_constructor" << endl; }
      ~B() { cout << "I am B_dtor" << endl; }
    };
    
    int main() {
      Stub stub;
      auto xa = get_dtor_addr<A>();
      auto xb = get_dtor_addr<B>();
      stub.set(xa, xb);
      A aa;
      return 0;
    }
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51
    • 52
    • 53
    • 54
    • 55
    • 56
    • 57
    • 58
    • 59
    • 60
    • 61

    1.16 默认构造函数打桩

    //for linux
    #include
    #include "stub.h"
    using namespace std;
    
    
    template<class T>
    void * get_ctor_addr(bool start = true)
    {
    	//the start vairable must be true, or the compiler will optimize out.
        if(start) goto Start;
    Call_Constructor:
        //This line of code will not be executed.
    	//The purpose of the code is to allow the compiler to generate the assembly code that calls the constructor.
        T();
    Start:
        //The address of the line of code T() obtained by assembly
        char * p = (char*)&&Call_Constructor;//https://gcc.gnu.org/onlinedocs/gcc/Labels-as-Values.html
        //CALL rel32
    	void * ret = 0;
    	char pos;
    	char call = 0xe8;
    	do{
    		pos = *p;
    		if(pos == call)
    		{
    			ret = p + 5 + (*(int*)(p+1));
    		}
    		
    	}while(!ret&&(++p));
        
        return ret;
    }
    
    
    class A {
    public:
        A(){cout << "I am A_constructor" << endl;}
    };
    
    class B {
    public:
        B(){cout << "I am B_constructor" << endl;}
    };
    
    
    int main()
    {
        Stub stub;
        auto xa = get_ctor_addr<A>();
        auto xb = get_ctor_addr<B>();
        stub.set(xa, xb);
        A aa;
        return 0;
    }
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51
    • 52
    • 53
    • 54
    • 55
    • 56

    1.17 使用AddrAny对lambda函数打桩和对任意函数打桩暂缺有问题

    四、 cpp-stub-ext使用(使用lambda打桩,Optional)

    基于cpp-stub的功能扩展模块,提供更加丰富,方便的功能。 更加高效地在单元测试中打桩。

    基于Linux 系统与 gcc/g++编译器开发

    使用说明:

    使用lamda函数作为打桩函数,需调专用接口StubExt::set_lamda。

    lamda函数可以使用参数和无参数:

    为常规函数打桩,lamda有参数时lamda函数的参数列表必须与被打桩的函数参数一致。
    为成员函数打桩,lamda有参数时lamda函数的第一个参数必须为该类的指针,接着是该成员函数的参数列表。
    为常规函数打桩,lamda无参数可直接使用。
    为成员函数打桩,lamda无参数可直接使用。

    代码有问题,目前暂且不适使用

    五、Stub cpp和Gtest结合使用

    // Test framework
    #include "gmock/gmock.h"
    #include "gtest/gtest.h"
    
    // Stub
    #include "stub.h"
    
    #include 
    
    class Obj {
    public:
      Obj() { m_i = 1; }
      virtual bool is_valid(std::string str) { return str.find("test") == 0; }
    
      int get_number() { return m_i; }
    
    private:
      int m_i;
    };
    
    bool check_obj(Obj &obj) {
      if (obj.is_valid("Kanotest")) {
        std::cout << "invalid" << std::endl;
        return false;
      }
    
      std::cout << obj.get_number() << std::endl;
      return true;
    }
    
    // 可以放在mocks/目录下
    class MockObj : public Obj {
    public:
      MOCK_METHOD1(is_valid, bool(std::string));
      MOCK_METHOD0(get_number, int());
    };
    
    // Add useful testing functions to namespace
    using ::testing::_;
    using ::testing::AtLeast;
    using ::testing::Exactly;
    using ::testing::NiceMock;
    using ::testing::Return;
    
    // stub function
    // 可以放在stub/目录下
    int get_number_stub(void *obj) { return 5; }
    
    // ========================可以放在fixtures/目录下
    
    class StringsFixture : public ::testing::TestWithParam<std::string> {
    public:
      virtual void SetUp() {
        // Do some setup
      }
      virtual void TearDown() {
        // Do some tear down
      }
    };
    
    // 宏接受三个参数:测试套件的名称、测试类的名称和参数值列表
    INSTANTIATE_TEST_SUITE_P(StringsFixtureSuite, // Instantiation name
                             StringsFixture,      // Fixture controller
                             ::testing::Values(   // Parameters
                                 "Kano", "KanoTest", "Kano123", "123Kano", "^$K@",
                                 "Kano%£("));
    
    // ================================================end
    
    TEST(get_number_test, returns_correct_value) {
      Stub stub;
      stub.set(ADDR(Obj, get_number), get_number_stub);
      Obj obj;
      ASSERT_EQ(obj.get_number(), 5);
    }
    
    TEST(ObjTest, test_obj_operations_with_mocks) {
      NiceMock<MockObj> obj;
      EXPECT_CALL(obj, is_valid(_)).Times(Exactly(1)).WillRepeatedly(Return(false));
    
      // get_number并不是虚函数哦
      ON_CALL(obj, get_number()).WillByDefault(Return(7));
    
      EXPECT_TRUE(check_obj(obj));
    }
    
    TEST_P(StringsFixture, test_string_valid) {
      // Retrieve the test parameter from the fixture
      auto test_str = GetParam();
    
      std::cout << "test_str=" << test_str << std::endl;
      Obj obj;
      ASSERT_EQ(obj.is_valid(test_str), test_str.find("Kano") == 0);
    }
    
    // Reference:  https://github.com/KanoComputing/googletest-sample
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51
    • 52
    • 53
    • 54
    • 55
    • 56
    • 57
    • 58
    • 59
    • 60
    • 61
    • 62
    • 63
    • 64
    • 65
    • 66
    • 67
    • 68
    • 69
    • 70
    • 71
    • 72
    • 73
    • 74
    • 75
    • 76
    • 77
    • 78
    • 79
    • 80
    • 81
    • 82
    • 83
    • 84
    • 85
    • 86
    • 87
    • 88
    • 89
    • 90
    • 91
    • 92
    • 93
    • 94
    • 95
    • 96

    编译并运行

    root@65147725a2cb:/opt/test_gtest# g++ test1.cc -l gtest -lgmock -lgtest_main -lpthread -I ../cpp-stub/src/
    root@65147725a2cb:/opt/test_gtest# ./a.out 
    Running main() from /build/googletest-j5yxiC/googletest-1.10.0/googletest/src/gtest_main.cc
    [==========] Running 8 tests from 3 test suites.
    [----------] Global test environment set-up.
    [----------] 1 test from get_number_test
    [ RUN      ] get_number_test.returns_correct_value
    [       OK ] get_number_test.returns_correct_value (0 ms)
    [----------] 1 test from get_number_test (0 ms total)
    ...
    ...
    ...
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12

    参考

  • 相关阅读:
    hbuilder x配置 配置使用 vue-cli和微信开发者工具
    智慧法院新征程,无纸化办公,护航智慧法院绿色庭审
    第三次上机作业 大连理工大学
    【linux编程】linux文件IO高级I/O函数介绍和代码示例
    百度百科数据爬取 python 词条数据获取
    @Async异步失效的9种场景
    CentOS停更在即,国内厂商该如何应对?KeyarchOS X2Keyarch 迁移体验
    记一次 Visual Studio 2022 卡死分析
    BGP服务器租用腾讯云和阿里云价格对比
    巧用excel实现试卷向表格的转换
  • 原文地址:https://blog.csdn.net/u011436427/article/details/133130512