前言
??在进行单元测试时,我们想要测试自己缩写函数A ,但是函数A 却依赖于函数B ,当函数B 无法满足预期时就无法对函数A 进行测试,主要由于下面几个原因:
函数B 依赖于硬件设备- 真实的
函数B 的返回值无法满足我们的预期 - 团队开发中
函数B 尚未实现
??这时就需要对函数B 进行打桩(仿真mock ),使其达到我们预期的效果。
但是如同下面这种类型函数时,则需要深入下去,对基层的函数进行打桩:
typedef struct
{
int num;
char *ptr;
char bot[20];
} ST_OBJ;
int fun_A()
{
fun_B();
return 0;
}
void fun_B(ST_OBJ *ptObj)
{
fun_C(ptObj);
fun_D(ptObj);
fun_E(ptObj);
}
1. 下载网址
https://github.com/google/googletest
2. 为什么选择Google Mock
- 首先当然是我们遇到了在前言中所说的问题。
- 其次Goole的Mock也是代码,也是工程师写的,自己当然也可以去实现,但费时费力,难以避免出错,对于初学者很难。
- 再有就是Google Mock使用方便,只要
#include"gtest/gtest.h"和"Mockpp.h" ,你已经准备好了。 - 且功能强大。
3. C中简单的例子
要测试的函数:
int fun_other(int num)
{
num += 50;
return num;
}
int fun_greater100(int num)
{
if(fun_other(num) <= 100){
printf("not greate\n");
return FALSE;
}
printf("not greate\n");
return TRUE;
}
编写TEST_F测试
#include "gtest.h"
#include "mockpp.h"
class test_class : public testing::Test
{
public:
virtual void SetUp() {}
virtual void TearDown() {}
};
TEST_F(test_class, mocktest)
{
MOCKER(fun_other)
.expects(once())
.will(returnValue(1));
int testnum = 88;
ASSERT_EQ(FALSE, fun_greater100(testnum));
}
#include <gtest.h>
int main(int argc, char **argv)
{
testing::InitGoogleTest(&argc, argv);
return RUN_ALL_TEST();
}
4. 指定自己的期望
??可以看出Mock和Stub的差异,Mock可以像零件式的个性化定制打桩需求,自己并不需要去实现打桩函数。 ??测试中每个用例对打桩函数的预期是不同的,如果采用Stub ,则需要定义很多打桩函数并实现他。
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")]
}
注:
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的作用是进行定制化的入参检查,比如只检查结构体的部分成员。可以通过函数指针或者仿函数的方式指定,用仿函数还能预先保存一些值,非常方便。
7、check也能够用于设置出参的情况。
5. 总结
5.1 Mock使用
Mock 普通函数使用MOCKER Mock 类成员方法先用MOCKER mocker ;声明一个mock 对象,再用MOCK_METHOD(mocker, method)
5.2 校验
紧跟着MOCKER/MOCK_METHOD之后的是stubs、或者defaults、或者expects,三个必须有一个。这个层次上确定这三个关键字必须有一个,可以让mock语法更清晰)
- stubs 表示指定函数的行为,不校验次数。
- expects 与stubs的区别就是校验次数。(.expects(once()) / .expects(never()) / .expects(exactly(123))
- defaults 表示定义一个默认的mock规范,但它优先级很低;如果后面有stubs或者expects重新指定函数行为,就会按照新指定的来运行。
5.3 期望
-
用will指定函数的返回值; -
如果要指定20次调用都返回1,则用.will(repeat(1, 20)); -
要指定第一次返回1,第二次返回2,第三次返回3,就用 .will(returnValue(1)) .then(returnValue(2)) .then(returnValue(3)) 如果你指定了前三次的返回值依次为1、2、3,那么第四次、第五次调用,都是返回最后的返回值3。
5.4 顺序
用id给一个mock规范指定一个名字,然后可以用after、before来指定多个mock应该的调用顺序。 注意before在with前,after在with后,id在整个mock规范的最后。
参数解释详见MockC++
|