单元测试工具GTest/Gmock(二)
接前文。
Gmock是google开发的一套辅助测试的工具,它往往和GTest结合在一起使用。在实际工作中,一个人不可能完成整条线的开发工作。于是我们会在约定接口的前提下,各自完成各自的模块。自己的模块开发完之后,我们需要自测。但是这个时候别人的模块可能还没完成,那么我们就需要模拟约定的接口进行自测。Gmock就是一个强大的模拟接口的工具。
class User
{
public:
User(){};
~User(){};
public:
// 登录
virtual bool login(const std::string &username, const std::string &password) = 0;
// 支付
virtual bool pay(int money) = 0;
// 是否登录
virtual bool online() = 0;
};
// 业务模块要让用户登录,并发起支付行为。
class Biz
{
public:
void setUser(User *user)
{
user_ = user;
}
std::string pay(const std::string &username, const std::string &password, int money)
{
std::string ret;
if (!user_)
{
ret = "pointer is null.";
return ret;
}
if (!user_->online())
{
ret = "logout status. ";
// 尚未登录,要求登录
if (!user_->login(username, password))
{
// 登录失败
ret += "login error. ";
return ret;
}
else
{
// 登录成功
ret += "login success. ";
}
}
else
{
ret = "login status. ";
}
if (!user_->pay(money))
{
ret += "pay error. ";
}
else
{
ret += "pay success. ";
}
return ret;
}
private:
User *user_;
};
这段代码逻辑就是先看看用户登录了没,如果没有登录则要求用户登录。如果登录失败,则直接返回;如果登录成功,则执行支付行为。最后将流程的状态输出。
那么怎么使用Gmock呢。
第一步:需要Mock接口类
class MockUser : public User
{
public:
MOCK_METHOD0(online, bool());
MOCK_METHOD2(login, bool(const std::string &, const std::string &));
MOCK_METHOD1(pay, bool(int));
};
MOCK_METHOD后跟一个数字,该数字表明需要mock的函数有几个参数。
MOCK_METHOD系列宏的第一个参数是函数名,第二个参数是函数指针的类型。
第二步:我们就可以设计测试场景了。在设计场景之前,我们先看一些Gmock的方法。
EXPECT_CALL(mock_object, Method(argument-matchers))
.With(multi-argument-matchers)
.Times(cardinality)
.InSequence(sequences)
.After(expectations)
.WillOnce(action)
.WillRepeatedly(action)
.RetiresOnSaturation();
where all clauses are optional, and .InSequence()/.After()/
.WillOnce() can appear any number of times.
- EXPECT_CALL声明一个调用期待,就是我们期待这个对象的这个方法按什么样的逻辑去执行。
- mock_object是我们mock的对象,上例中就是TestUser的一个对象。
- Method是mock对象中的mock方法,它的参数可以通过argument-matchers规则去匹配。
- With是多个参数的匹配方式指定。
- Times表示这个方法可以被执行多少次。如果超过这个次数,则按默认值返回了。
- InSequence用于指定函数执行的顺序。它是通过同一序列中声明期待的顺序确定的。
- After方法用于指定某个方法只能在另一个方法之后执行。
- WillOnce表示执行一次方法时,将执行其参数action的方法。一般我们使用Return方法,用于指定一次调用的输出。
- WillRepeatedly表示一直调用一个方法时,将执行其参数action的方法。需要注意下它和WillOnce的区别,WillOnce是一次,WillRepeatedly是一直。
- RetiresOnSaturation用于保证期待调用不会被相同的函数的期待所覆盖。
现在模拟正常的流程:
TEST(MyTest, PayTest)
{
MockUser test_user;
EXPECT_CALL(test_user, online()).WillOnce(testing::Return(false));
EXPECT_CALL(test_user, login(testing::_, testing::_)).WillRepeatedly(testing::Return(true));
EXPECT_CALL(test_user, pay(testing::_)).WillOnce(testing::Return(true));
Biz biz;
biz.setUser(&test_user);
std::string pay_result1 = biz.pay("admin", "123", 100);
cout << pay_result1 << endl; //
}
EXPECT_CALL(test_user, online()).WillOnce(testing::Return(false));
表示调用online
方法返回true
EXPECT_CALL(test_user, login(testing::_, testing::_)).WillRepeatedly(testing::Return(true));
表示调用login
方法一直返回true,这里testing::_
通配符,表示对任何输入参数都按之后要求执行。
EXPECT_CALL(test_user, pay(testing::_)).WillOnce(testing::Return(true));
表示调用pay
返回true。
编译:
g++ -o test mock1.cpp -lgmock -lgtest_main -lpthread
输出:
image.png
输出结果,表明我们完全模拟出了业务代码种正常支付逻辑,用户没有登录->用户成功登录->支付成功。
参考资料