上一篇文章(C++开发测试工具gmock使用详解——对抽象接口类进行gmock打桩并测试)主要介绍了对抽象接口类进行gmock打桩的基本流程并利用简单Demo进行测试。本文将介绍gmock打桩的进阶案例,结合上文一起食用效果更佳哦~
当开发者在单元测试、模块接口测试时,当这个模块需要依赖另外一个/几个类,而这时这些个类还没有开发好(因为某些原因),这时我们就可以定义了Mock对象来模拟那些没开发好类的行为。
Mock框架可以帮助比较方便、轻松地实现这些假的依赖类。
接下来介绍一下gmock的进阶代码,加深大家对于gmock打桩的了解。
进阶代码实现海龟turtle运动的一个程序。
与其让我编写的应用程序直接与系统 API 通信,不如将 API 包装在一个接口中(interface),然后编写代码实现该接口:
//接口
class Turtle {
public:
//控制turtle运动是否留下痕迹
virtual void PenUp() = 0; //画笔抬起
virtual void PenDown() = 0; //画笔落下
//控制turtle运动方向
virtual void Forward(int distance) = 0; //前进
virtual void Turn(int degrees) = 0; //转向
virtual void GoTo(int x, int y) = 0; //前往(x,y)位置
//获取turtle当前位置
virtual int GetX() = 0; //获取x的坐标
virtual int GetY() = 0; //获取y的坐标
public:
virtual ~Turtle() {} //析构函数必须是virtual
};
在该接口中,使用PenUp()、PenDown()来控制turtle的画笔抬起与落下,使用Forward()、Turn()和GoTo()控制其运动,最后,使用GetX()和GetY()获取当前位置。
【注意】:析构函数必须是虚拟的virtual,因为打算从中继承所有类,否则当你通过基指针删除对象时,将不会调用派生类的析构函数,而且会获得损坏的程序状态,例如内存泄露。
正常情况下,小A同学应该编写代码实现上述接口中的7个函数,但是由于种种原因(可能是小A同学比较懒或者是被隔离了没带电脑)导致部分代码没有编写完成,因此这时就需要Mock发挥作用。
小A同学编写的代码如下:
/*
...
*/
(实际上什么也没写)
我的任务是根据小A同学编写完成的代码进行实现更高阶的相关功能。比如利用小A同学的上述相关接口检查是否可以绘制圆形(三点绘制一个圆),以及计算当前点x+y的数值,代码如下:
class Painter
{
Turtle* turtle;
public:
Painter(Turtle* turtle)
: turtle(turtle) {}
bool DrawCircle(int, int, int) {
turtle->PenDown();
return true;
}
int DrawXandY() {
int x = turtle->GetX();
int y = turtle->GetY();
return x + y;
}
};
正常情况下,我的程序通常会使用小A同学编写完成的代码进行实现。但是在测试中,可以使用Mock进行代替。这样就可以轻松检查程序正在调用的程序、参数、顺序等。Mock代码如下:
class MockTurtle : public Turtle {
public:
MOCK_METHOD0(PenUp,void());
MOCK_METHOD0(PenDown,void());
MOCK_METHOD1(Forward,void(int distance));
MOCK_METHOD1(Turn,void(int degrees));
MOCK_METHOD2(GoTo,void(int x, int y));
MOCK_METHOD0(GetX,int());
MOCK_METHOD0(GetY,int());
};
实现步骤:
使用gmock中的一个宏(macro)MOCK_METHOD来定义Turtle中的函数。
MOCK_METHOD定义如下:
MOCK_METHOD#1(#2, #3(#4) )
定义Mock类时,需要决定将其放在何处,有些人将其放在cpp源文件中,当Mock界面由同一个人编写时,这可以,但是当所有者更改它时,测试可能会中断。因此建议单独放在一个h文件中。
在Test中使用Mock类的基本流程是:
using ::testing::AtLeast; //#1
using ::testing::Return;
TEST(PainterTest, CanDrawSomething) {
MockTurtle turtle; // #2
EXPECT_CALL(turtle, PenDown()) // #3
.Times(AtLeast(1));
Painter painter(&turtle);
EXPECT_TRUE(painter.DrawCircle(0, 0, 10)); // #4
EXPECT_CALL(turtle, GetX()) // #3
.WillOnce(Return(10)); //x返回10
EXPECT_CALL(turtle, GetY()) // #3
.WillOnce(Return(20)); //y返回20
EXPECT_EQ(30, painter.DrawXandY()); //#4
}
EXPECT_CALL(turtle, PenDown()).Times(AtLeast(1));
表示此测试至少会检查一次调用,如果对象未调用此方法,则测试将失败。
main函数中代码如下:
int main(int argc, char **argv)
{
testing::InitGoogleTest(&argc, argv);
return RUN_ALL_TESTS();
}
完整代码可以参考:https://download.csdn.net/download/didi_ya/83470301
ok,以上便是本文的全部内容了,如果对你有所帮助,记得点个赞哟~
参考: