mockcpp使用方法简明指导

mockcpp使用方法简明指导

mock工具介绍

mock工具的作用是指定函数的行为(模拟函数的行为)。可以对入参进行校验,对出参进行设定,还可以指定函数的返回值。

几个相关概念

(1)mock规范:每个MOCKER(function)开始,跟一系列的.stubs、.with、.will等的内容的整体,称为一个mock规范。

(2)核心关键字:指stubs/defaults/expects/before/with/after/will/then/id等这些直接跟在点后面的关键字。

(3)扩展关键字:指once()/eq()/check()/returnValue()/repeat()等这些作为核心关键字参数的关键字。 下面,请看两段mockcpp的使用规范示例代码,其中带“/”或者“|”的表示在该位置可能有多种选择;带中括号的表示是可选的。

mockcpp的特点

(1) 开源。

(2) mockcpp支持C函数的mock和虚接口的mock。

(3) VC下,mockcpp支持cdecl和stdcall调用约定的函数。(socket/bind/ftp等函数就是stdcall调用约定的)

(4) mockcpp的语法清晰、简单,容易理解(参见下面两个sample)。

(5) mockcpp的错误信息提示非常友好,包含完整的mock规范定义和实际运行情况(参见下面的样例)。

Unexpected invocation: the invocation cannot be found in allowed invoking list.
Invoked: add((int)0x1/1, (int)0x2/2)
Allowed:
method(add)
  .stubs()
  .invoked(0)
  .with(eq((int)0xa/10), eq((int)0x14/20))
  .will(returnValue((int)0xc8/200));

(9) mockcpp是强类型检查的,强类型检查也是C/C++的一个优势,比如eq(3),如果调用函数时用的参数是UL,那么就应该用eq((UL)3),对于函数返回值也是一样,声明的返回值类型应该跟mock中定义的一致。mock中支持类型检查,可能会发现更多代码BUG的。

C++强类型检查,也是为了提高程序安全性,有些问题通过类型检查在编译期可以发现的,就不需要在运行时再痛苦的定位了。C/C++的强类型检查是优势,我们不用把它抛弃了。

mockcpp的sample代码

下面,请看两段mockcpp的使用规范示例代码,其中带“/”或者“|”的表示在该位置可能有多种选择;带中括号的表示是可选的。

一段简单的mockcpp使用sample代码:(带有完整核心关键字)

TEST(mockcpp simple sample)
{
    MOCKER(function) / MOCK_METHOD(mocker, method)
        .stubs() / defaults() / expects(once())
        [.before("some-mocker-id")]
        [.with(eq(3))]
        [.after("some-mocker-id")]
        .will(returnValue(1)) / .will(repeat(1, 20))
        [.then(returnValue(2))]
        [.id("some-mocker-id")]
}

下面是一段比较详细的mockcpp使用sample代码:(带有完整扩展关键字)

TEST(mockcpp detail sample)
{
    MOCKER(function) / MOCK_METHOD(mocker, method)
        .stubs() / defaults() / expects(never() | once() | exactly(3) | atLeast(3) | atMost(3) )
        [.before("some-mocker-id")]
        [.with( any() | eq(3) | neq(3) | gt(3) | lt(3) | spy(var_out) | check(check_func)
                | outBound(var_out) | outBoundP(var_out_addr, var_size) | mirror(var_in_addr, var_size)
                | smirror(string) | contains(string) | startWith(string) | endWith(string) )]
        [.after("some-mocker-id")]
        .will( returnValue(1) | repeat(1, 20) | returnObjectList(r1, r2)
                | invoke(func_stub) | ignoreReturnValue()
                | increase(from, to) | increase(from) | throws(exception) | die(3))
        [.then(returnValue(2))]
        [.id("some-mocker-id")]
}

1、扩展关键字分类: expects里面的叫匹配关键字(Matcher); with里面的叫约束关键字(Constraint); will/then里面的叫桩关键字(Stub)。

2、spy的作用是监视执行该被mock的函数function被调用时传入的值,会保存在var_out中,供用例中其它地方使用。

3、outBound的作用是设置函数function的出参的值。多半是把该值作为后面部分被测代码的输入。(注意与spy区别)

4、outBoundP,与outBound作用相同,只是用于数组的情况。

5、mirror的作用是对数组类型的入参进行检查。(outBoundP是设置出参的值,两者是不同的)

6、check的作用是进行定制化的入参检查,比如只检查结构体的部分成员。可以通过函数指针或者仿函数的方式指定,用仿函数还能预先保存一些值,非常方便。(有些mock工具叫它follow)

7、check也能够用于设置出参的情况。

8、die表示程序退出,并且返回指定的值。它是异常退出,用于模拟一个函数调用崩溃的情况。

9、increase(from, to),表示依次返回from到to的对象,任何重载了++运算符的对象都可以用。

10、outBound和outBoundP都可以带一个约束参数,用于对参数进行检查,因为有同时作为in和out的参数。(如:outBound(var_out, eq(3)) )

下面是结合上面sample的mockcpp使用说明

1、mock C函数或者类的静态成员方法用MOCKER;

mock 类的非静态成员方法需要先用 MockObject<MyClass> mocker;声明一个mock对象,再用MOCK_METHOD(mocker, method)来mock指定方法。

2、紧跟着MOCKER/MOCK_METHOD之后的是stubs、或者defaults、或者expects,三个必须有一个。(这是与AMOCK不同的地方,在这个层次上确定这三个关键字必须有一个,可以让mock语法更清晰)

stubs 表示指定函数的行为,不校验次数。 expects 与stubs的区别就是校验次数。(.expects(once()) / .expects(never()) / .expects(exactly(123))) defaults 表示定义一个默认的mock规范,但它优先级很低;如果后面有stubs或者expects重新指定函数行为,就会按照新指定的来运行。(一般用在setup中)

3、用will指定函数的返回值;

如果要指定20次调用都返回1,则用.will(repeat(1, 20)); 要指定第一次返回1,第二次返回2,第三次返回3,就用

      .will(returnValue(1))
      .then(returnValue(2))
      .then(returnValue(3))
如果你指定了前三次的返回值依次为1、2、3,那么第四次、第五次调用,都是返回最后的返回值3。

4、用id给一个mock规范指定一个名字,然后可以用after、before来指定多个mock应该的调用顺序。

注意before在with前,after在with后,id在整个mock规范的最后。
5、使用mockcpp时,校验是否按照mock规范进行调用的,应该用: GlobalMockObject::verify(); verify之后,会自动执行reset。(如果是对象的mock,应该用mocker.verify(),同样也会自动reset。)

如果单单只想reset,也可以:(这很少见,难道前面你定义的mock规范都不想要了,也不校验?那为何要定义呢?) GlobalMockObject::reset();

一般是在teardown中调用verify。
6、如果要对某个函数(比如add)指定多个调用不同的行为(多个mock规范),比如调用入参为1,2时,返回30;调用入参为3、4时,返回700。那么可以这样写:

MOCKER(add)
  .stubs()
  .with(eq(1), eq(2))
  .will(returnValue(30));

MOCKER(add)
  .stubs()
  .with(eq(3), eq(4))
  .will(returnValue(700));

7、如果对于6的例子,还希望校验次数,则把stubs改为expects(once())这样的即可。

8、如果对于6的例子,还希望对调用顺序做严格要求,必须第一次调用参数是1、2,第二次是3、4,那么可以这样写:

MOCKER(add)
  .stubs()
  .with(eq(1), eq(2))
  .will(returnValue(30))
  .id("first");

MOCKER(add)
  .stubs()
  .with(eq(3), eq(4))
  .after("first")
  .will(returnValue(700));

9、指定出参的方法(例子中出参为指针类型,出参为引用的outBound(var)即可,更加简单)

void function(int *val)
{
}

应该用下面方式来指定出参:

int expect = 10;
MOCKER(function)
  .stubs()
  .with(outBoundP(&expect, sizeof(expect)));

10、对同一个函数指定多个mock规范,那么这些mock规范一般是应该在输入方面有差异的,否则没必要指定多个。而且,在输入方面无差异的两个mock规范,让mockcpp内部无法判断是否满足规范约束。 比如,测试函数 void function(int in, int &out),前面一个是入参,后面一个是出参,希望in为1时,输出out为100,in为2时,输出out为3,那么应该这样写:

int out = 100;
MOCKER(function)
  .expects(once())
  .with(eq(1), outBound(out));

out = 3;
MOCKER(function)
  .expects(once())
  .with(eq(2), outBound(out));

假设你把eq(1)、eq(2)写为any(),那么mockcpp会判断你的两个调用function(1, out)和function(2, out)都符合第一个mock规范,从而报告与你定义的次数1不匹配。

11、有时候需要对同一个函数指定多个mock规范,并且它们是有规律的,那么可以借助循环来简化代码。 假设要mock的函数是void function(int in),希望它依次以0、1、2...10为入参被调用,每次都调用一次,那么可以像这样写:

MOCKER(function)
  .expects(once())
  .with(eq(0))
  .id("0");

for (int i = 1; i <= 10; i++)
{
  MOCKER(function)
    .expects(once())
    .with(eq(i))
    .after(string(i - 1))
    .id(string(i));   
}

12、spy约束关键字应用举例。 spy的作用是监视,下面例子,就是监视test在调用func时,传入的值是多少。有些时候,测试用例需要这样的值,这种方式是很有效的。

void func(int var)
{
}
void test()
{
    int var = 10;
    func(var);
}
TEST(sample test)
{
    int var;
    MOCKER(func)
        .stubs()
        .with(spy(var));
        .with(eq(10));

    test();
    ASSERT_EQ(var, 10);    
}

13、check约束关键字的用法。 约束关键字中,有很多都是对入参进行检查的,比如eq、neq、lt、gt等等,但它们都只实现了非常简单的检查。 如果用户想做一个定制化的检查,那么就可以用check。 例1:假设用户想检查入参p指向的结构的字段b是不是10,那么可以如下这样写:

struct AA 
{
    int a;
    int b;
};

void func(int in, AA *p)
{
}

// 实现一个检查函数,入参类型跟要检查的入参相同,返回值为bool,返回true表示检查通过。
bool check_func(AA *p)
{
    if ( p->b == 10)
    {
        return true;
    }
    return false;
}

TEST(sample test)
{
    MOCKER(func)
        .stubs()
        .with(any(), check(check_func));
    func(in, p);
}

例2:如果需要检查b是否小于某个给定的数,而且有多个用例,每个用例中与b比较的数不同,则可以使用仿函数的方式。

// 实现一个仿函数类,即重载“()”运算符的类。
struct CheckFunctor 
{
    CheckFunctor(int _base) : base(_base){}
    bool operator ()(AA *p)
    {
        if (p->b < base)
               return true;
        return false;
    }
    int base;
};

TEST(b should less than 100)
{
    MOCKER(func)
        .stubs()
        .with(any(), check(CheckFunctor(100)));
    func(in, p);
}

TEST(b should less than 200)
{
    MOCKER(func)
        .stubs()
        .with(any(), check(CheckFunctor(200)));
    func(in, p);
}

14、只要打桩了,那么就不会运行原函数,都是与mock规范匹配,运行桩。 下面例子中,对add的入参为1、2的情况打桩了,如果调用了入参为2、3的情况,则会报告调用在允许的mock规范列表中不存在的错误。

TEST(sample test)
{
    MOCKER(add)
        .expects(once())
        .with(eq(1), eq(2))
        .will(returnValue(100));

    ASSERT_EQ(100, add(1, 2));
    ASSERT_EQ(50, add(2, 3));
}

15、带有返回值的函数,MOCKER后面必须有will,否则mockcpp认为无返回值,校验时会发现返回类型不匹配。

  • 5
    点赞
  • 56
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
### 回答1: 《数值方法简明教程》是一本介绍数值方法的中文教材,旨在帮助读者快速理解和应用数值方法。该教程主要内容包括数值解技术的基本原理、数值计算的误差分析、常见的数值计算方法以及相关的数值计算软件等。 该教程以简明易懂的语言和实例,系统地介绍了数值方法的基础知识。首先,它详细讲解了数值方法的基本概念和数值计算的误差来源,使读者对数值计算有一个清晰的认识。接着,教程介绍了常见的数值计算方法,如插值与逼近、数值积分、数值微分、线性代数方程的数值解等。每种方法都配有具体的算法步骤和实例说明,帮助读者理解方法的原理和实现过程。 此外,教程还介绍了常用的数值计算软件,并提供了相关的应用案例。这些软件包括MATLAB、Python等,它们在数值计算领域有着广泛的应用和支持。教程通过实例展示了如何使用这些软件进行数值计算,并给出了一些常见问题的解决方法,使读者能够迅速上手并运用所学知识。 总之,《数值方法简明教程》以简洁明了的语言,给读者提供了一个系统学习和掌握数值方法的机会。通过学习教程,读者可以了解数值方法的基本原理和应用技巧,掌握数值计算的基本知识和方法,提高数值计算问题的求解能力。无论是在科学研究、工程建模还是其他领域,这本教程都是一本宝贵的参考书。 ### 回答2: 《数值方法简明教程PDF》是一本介绍数值方法简明教程,以PDF格式出版。数值方法是一种通过数值计算近似解的方法,广泛应用于科学、工程和金融等领域。这本教程旨在帮助读者快速理解和掌握数值方法的基本概念和应用。 教程的内容包括数值解析、数值逼近、数值积分、常微分方程数值解、偏微分方程数值解等。首先,教程介绍了数值方法的背景和发展历史,以便读者对其有一个整体的了解。然后逐步介绍了数值方法的基本思想和常用技巧,如插值、牛顿法、高斯消元法等。 教程使用简洁清晰的语言,配以图例和实例,使读者能够快速掌握数值方法的要点和实际应用。同时,教程还提供了习题和答案,供读者进行练习和巩固所学知识。 通过学习《数值方法简明教程PDF》,读者可以了解数值方法的基本原理和计算过程,掌握数值计算的基本技巧和方法,以及理解数值计算在实际问题中的应用。这对于从事科学、工程和金融等领域的研究者和实践者都具有重要的参考价值。教程简明扼要地介绍了数值方法的核心概念,可以帮助读者快速入门和提高计算效率。 综上所述,《数值方法简明教程PDF》通过清晰简洁的内容和实例,帮助读者快速理解和应用数值方法,是一本实用的学习资料。无论是初学者还是有一定基础的研究者,都可以从中获得实际帮助。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值