第一篇文章(C++开发测试工具gmock的安装与使用超详解)和第二篇文章(C++开发测试工具gmock的使用详解)分别对gmock的安装、配置与使用案例进行了详细介绍,本文将对抽象接口类进行gmock打桩测试进行介绍,结合上文一起食用效果更佳哦~
首先来编写一个简单Demo,编写代码之前,首先需要将解决方案的平台与目标程序一致,这里选择的是x64,然后版本也要与目标程序一致,这里选择的是release版本。
假设我和小A共同开发一款产品,他负责对不同的图形计算面积,而我负责根据面积进行相关处理(简单乘以一个固定系数)。
首先我们需要共同约定定义一个接口(公共成员变量Area):
class Shape {
public:
virtual double Area(void) = 0;
};
小A的任务是根据不同的类型进行计算,例如计算矩形面积,代码如下:
//小A任务
class Rectangle : public Shape {
private:
double width, height;
public:
//构造函数
Rectangle(double w, double h)
{
width = w;
height = h;
}
// 析构函数
~Rectangle() { } // Do nothing
//计算矩形面积
double Area(void)
{
return width * height;
}
};
//我的任务是根据小A计算得到的结果面积进行相关处理(简单乘以一个固定系数,这里以2为例):
double GetPrice(class Shape &s)
{
return s.Area() * 2;
}
正常情况下,在main函数中应该调用小A编写的代码获取结果面积,然后再调用我的代码对面积进行相关处理,将处理后的结果打印输出:
int main(void)
{
Rectangle rect(3, 4);
std::cout << GetPrice(rect) << std::endl;
return 0;
}
但是,由于小A需要完成三角形、梯形、圆等诸多形状面积开发,工作量很大,因此为了不阻塞自己的开发,可以通过gmock来模拟Shape接口返回的数据,代码如下:
//模拟Shape接口返回的数据
class ShapeMock : public Shape {
public:
MOCK_METHOD0(Area, double());
};
TEST(ShapeTest, Rectangle)
{
ShapeMock rect; //Mock object
EXPECT_CALL(rect, Area())
.WillRepeatedly(testing::Return(12));//每次调用都会返回12
EXPECT_EQ(24, GetPrice(rect));
}
int main(int argc, char **argv)
{
testing::InitGoogleTest(&argc, argv);
return RUN_ALL_TESTS();
}
通过上述流程,可以总结出在利用gmock进行Mock打桩时的一般步骤如下:
MOCK_METHOD
的宏定义接口;Mock Object
),用来模拟接口定义的行为;EXPECT_CALL
)设置Mock对象需要执行的动作;EXPECT或ASSERT
)成功使用Mock对象的关键是对其设置正确的期望expectations。如果将期望值设置得太严格,则测试将因不相关的更改而失败。如果将它们设置得太松,错误可能会溜走。希望恰到好处地做到这一点,以便测试可以准确地捕获您希望它捕获的错误类型。gMock提供了必要的方法来"恰到好处"地做到这一点。
在 gMock 中,我们使用宏来设置对Mock方法的期望。一般语法为:EXPECT_CALL()。
EXPECT_CALL用于定义成员函数的期望行为,常用语法如下:
EXPECT_CALL(mock_object, method_name[, matchers])
.Times(cardinality)
.InSequence(sequences)
.After(expectations)
.WillOnce(action)
.WillRepeatedly(action)
.RetiresOnSaturation();
宏的参数如下:
下面介绍一下.后各个语句的功能:
其他功能建议参考google mock官方文档。
来看以下代码实例:
EXPECT_CALL(rect, Area())
.Times(testing::AtLeast(5))
.WillOnce(testing::Return(100))
.WillOnce(testing::Return(150))
.WillRepeatedly(testing::Return(200));
这个期望行为的含义是:
【注】:调用几次expectation就需要调用几次下面的执行语句,这个是一一对应的:
EXPECT_CALL(rect, Area())
.WillOnce(testing::Return(1))
.WillOnce(testing::Return(2))
.WillOnce(testing::Return(3));
std::cout << GetPrice(rect) << std::endl;
std::cout << GetPrice(rect) << std::endl;
std::cout << GetPrice(rect) << std::endl;
在上面调用了三次期望,因此在下面也应该执行三次获取输出。执行次数利用语句Times指定。
如果你对参数的值不感兴趣,请改成参数_,这意味着“任何事情都会发生”:
using ::testing::_;
...
// Expects that the turtle jumps to somewhere on the x=50 line.
EXPECT_CALL(turtle, GoTo(50, _));
_我们称之为matchers(匹配器)。匹配器就像一个判定,可以测试参数是否是我们所期望的。您可以在任何需要函数参数的位置使用匹配器。
上述例子中,50也是匹配器,它与Eq(50)相同。关于匹配器的详细内容,可以参考matchers。
如果你不关心任何参数,而不是为每个参数指定,则可以省略参数列表:
// Expects the turtle to jump somewhere.
EXPECT_CALL(turtle, GoTo);
.Times(cardinality)
指定模拟函数调用的预期次数。
更重要的是,基数可以是"模糊的",就像匹配器一样。这允许用户准确地表达测试的意图。
参数cardinality表示预期调用的数量,可以是以下参数之一,全部在命名空间::testing中定义:
cardinality | 含义 |
---|---|
AnyNumber() | 函数可以调用任意次数 |
AtLeast(n) | 函数调用至少需要n次 |
AtMost(n) | 函数调用最多需要n次 |
Between(m,n) | 函数调用预期在m和n之间(包括m和n) |
Exactly(n)或n | 函数调用正好需要n次,如果n部位0,则调用不应发生 |
如果省略该语句,则googletest将按如下方式推断Times:
代码示例:
using ::testing::Return;
...
EXPECT_CALL(turtle, GetY())
.Times(4)
.WillOnce(Return(100));
这段代码表示显然有望被调用四次。但是,如果您认为每次都会返回100,请三思而后行!请记住,每次调用函数时都会使用一个子句,之后将执行默认操作。因此,正确的答案是,第一次将返回 100,但从第二次开始返回 0,因为返回 0 是函数的默认操作。
ok,以上便是本文的全部内容了,如果对你有所帮助,记得点个赞哟~
参考: