如何仅覆盖一个 EXPECT_CALL 的默认 ON_CALL 操作并稍后返回默认操作
How to override the default ON_CALL action for just one EXPECT_CALL and go back to the default action later
我想测试一下我系统的方法,它的return值部分取决于调用某种连接接口的return值。在大多数情况下,我希望 IConnection
到 return true
对它的 open(_, _)
方法的任何调用。除了在一种情况下,当我明确测试连接失败的情况时。
示例:
/*
* Some kind of network interface with method `open`
*/
class IConnection {
public:
IConnection() = default;
virtual ~IConnection() = default;
virtual bool open(const std::string& address, int port) = 0;
};
class ConnectionMock: public IConnection {
public:
MOCK_METHOD2(open, bool(const std::string& address, int port));
};
class MySystem {
public:
MySystem() = delete;
MySystem(std::shared_ptr<IConnection> connection): connection_(connection) {}
bool doSth() {
/*
* Do some things, but fail if connection fails
*/
bool connectionStatus = connection_->open("127.0.0.1", 6969);
if (!connectionStatus) {
return false;
}
// do other things
return true;
}
private:
std::shared_ptr<IConnection> connection_;
};
TEST(MySystemShould, returnFalseIfFailedToOpenConnectionAndTrueIfSucceeded) {
auto connectionMock = std::make_shared<NiceMock<ConnectionMock> >();
ON_CALL(*connectionMock, open(_, _)).WillByDefault(Return(true));
MySystem system(connectionMock);
// if I don't specify Times test fill fail, because WillOnce automatically sets Times(1)
EXPECT_CALL(*connectionMock, open(_, _)).Times(AnyNumber()).WillOnce(Return(false));
/*
* Commented code below is not a good solution - after expectation retires
* the test will fail upon subsequent calls
*/
//EXPECT_CALL(*connectionMock, open(_, _)).WillOnce(Return(false)).RetiresOnSaturation();
ASSERT_FALSE(system.doSth());
/*
* Code bellow allows me to avoid the warning
*/
//EXPECT_CALL(*connectionMock, open(_, _)).WillRepeatedly(Return(true));
ASSERT_TRUE(system.doSth());
}
我当前解决方案的问题是,当 EXPECT_CALL
覆盖变得饱和时,即使 gmock 返回到 ON_CALL
上指定的默认操作,每个后续调用 open(_, _)
导致以下警告:
GMOCK WARNING:
/whatever.cpp:105: Actions ran out in EXPECT_CALL(*connectionMock, open(_, _))...
Called 2 times, but only 1 WillOnce() is specified - taking default action specified at:
/whatever.cpp:103:
尽管我使用的是 NiceMock
。我可以通过用 WillRepeatedly(Return(true))
指定 EXPECT_CALL
来消除警告,但这是我在 ON_CALL
.
中的代码的重复
我想知道,我怎样才能覆盖 ON_CALL
指定的默认操作,只调用一次 IConnection::open
,然后返回到默认值,而不会导致 gmock 打印一个警告。完美的解决方案类似于:
EXPECT_CALL(*connectionMock, open(_, _)).WillOnce(Return(false)).DisableExpectationAfterSaturation();
但它不存在。 RetiresOnSaturation
没有像我希望的那样工作,因为它在饱和后未通过测试(与 ON_CALL
指定的操作不匹配)。
编辑 2
DoDefault()
- 功能接近问题中的要求。它指定 EXPECT_CALL
中的操作应返回到 ON_CALL
:
指定的默认操作
using ::testing::DoDefault;
// Default action
ON_CALL(*connectionMock, open(_, _)).WillByDefault(Return(true));
// returns true once and then goes back to the default action
EXPECT_CALL(*connectionMock, open(_, _)
.WillOnce(Return(false))
.WillRepeatedly(DoDefault());
初步回答
如果 IConnection::open
的 return 值取决于参数,您可以指定 ON_CALL
两次但使用不同的参数(或者更确切地说是参数而不是占位符):
ON_CALL(*connectionMock, open(_, _)).WillByDefault(Return(true));
ON_CALL(*connectionMock, open("BAD_ADDRESS", 20)).WillByDefault(Return(false));
因此,任何时候使用参数 "BAD_ADDRESS" 和 20 调用模拟方法 open
时,它将 return 为假,否则为真。
这是一个简单的例子:
using ::testing::_;
using ::testing::AnyNumber;
using ::testing::Return;
class A {
public:
virtual bool bla(int a) = 0;
};
class MOCKA : public A {
public:
MOCK_METHOD1(bla, bool(int));
};
TEST(ABC, aBABA) {
MOCKA a;
ON_CALL(a, bla(_)).WillByDefault(Return(false));
ON_CALL(a, bla(1)).WillByDefault(Return(true));
EXPECT_CALL(a, bla(_)).Times(AnyNumber());
EXPECT_TRUE(a.bla(1));
EXPECT_TRUE(a.bla(1));
EXPECT_TRUE(a.bla(1));
EXPECT_FALSE(a.bla(2));
EXPECT_FALSE(a.bla(3));
EXPECT_FALSE(a.bla(4));
}
编辑 1
我想现在我明白了问题,如果我明白了,那么解决方案就很简单了:
EXPECT_CALL(*connectionMock, open(_, _))
.Times(AnyNumber())
.WillOnce(Return(true))
.WillRepeatedly(Return(false));
当 ConnectionMock::open
在 MySystem::doSth
内部被调用时,它会调用一次 return true
然后总是 return false
无论如何论据是。在这种情况下,您也不需要指定 ON_CALL
。或者您是否确实需要使用 ON_CALL
而不是 EXPECT_CALL
来指定操作?
我想测试一下我系统的方法,它的return值部分取决于调用某种连接接口的return值。在大多数情况下,我希望 IConnection
到 return true
对它的 open(_, _)
方法的任何调用。除了在一种情况下,当我明确测试连接失败的情况时。
示例:
/*
* Some kind of network interface with method `open`
*/
class IConnection {
public:
IConnection() = default;
virtual ~IConnection() = default;
virtual bool open(const std::string& address, int port) = 0;
};
class ConnectionMock: public IConnection {
public:
MOCK_METHOD2(open, bool(const std::string& address, int port));
};
class MySystem {
public:
MySystem() = delete;
MySystem(std::shared_ptr<IConnection> connection): connection_(connection) {}
bool doSth() {
/*
* Do some things, but fail if connection fails
*/
bool connectionStatus = connection_->open("127.0.0.1", 6969);
if (!connectionStatus) {
return false;
}
// do other things
return true;
}
private:
std::shared_ptr<IConnection> connection_;
};
TEST(MySystemShould, returnFalseIfFailedToOpenConnectionAndTrueIfSucceeded) {
auto connectionMock = std::make_shared<NiceMock<ConnectionMock> >();
ON_CALL(*connectionMock, open(_, _)).WillByDefault(Return(true));
MySystem system(connectionMock);
// if I don't specify Times test fill fail, because WillOnce automatically sets Times(1)
EXPECT_CALL(*connectionMock, open(_, _)).Times(AnyNumber()).WillOnce(Return(false));
/*
* Commented code below is not a good solution - after expectation retires
* the test will fail upon subsequent calls
*/
//EXPECT_CALL(*connectionMock, open(_, _)).WillOnce(Return(false)).RetiresOnSaturation();
ASSERT_FALSE(system.doSth());
/*
* Code bellow allows me to avoid the warning
*/
//EXPECT_CALL(*connectionMock, open(_, _)).WillRepeatedly(Return(true));
ASSERT_TRUE(system.doSth());
}
我当前解决方案的问题是,当 EXPECT_CALL
覆盖变得饱和时,即使 gmock 返回到 ON_CALL
上指定的默认操作,每个后续调用 open(_, _)
导致以下警告:
GMOCK WARNING:
/whatever.cpp:105: Actions ran out in EXPECT_CALL(*connectionMock, open(_, _))...
Called 2 times, but only 1 WillOnce() is specified - taking default action specified at:
/whatever.cpp:103:
尽管我使用的是 NiceMock
。我可以通过用 WillRepeatedly(Return(true))
指定 EXPECT_CALL
来消除警告,但这是我在 ON_CALL
.
我想知道,我怎样才能覆盖 ON_CALL
指定的默认操作,只调用一次 IConnection::open
,然后返回到默认值,而不会导致 gmock 打印一个警告。完美的解决方案类似于:
EXPECT_CALL(*connectionMock, open(_, _)).WillOnce(Return(false)).DisableExpectationAfterSaturation();
但它不存在。 RetiresOnSaturation
没有像我希望的那样工作,因为它在饱和后未通过测试(与 ON_CALL
指定的操作不匹配)。
编辑 2
DoDefault()
- 功能接近问题中的要求。它指定 EXPECT_CALL
中的操作应返回到 ON_CALL
:
using ::testing::DoDefault;
// Default action
ON_CALL(*connectionMock, open(_, _)).WillByDefault(Return(true));
// returns true once and then goes back to the default action
EXPECT_CALL(*connectionMock, open(_, _)
.WillOnce(Return(false))
.WillRepeatedly(DoDefault());
初步回答
如果 IConnection::open
的 return 值取决于参数,您可以指定 ON_CALL
两次但使用不同的参数(或者更确切地说是参数而不是占位符):
ON_CALL(*connectionMock, open(_, _)).WillByDefault(Return(true));
ON_CALL(*connectionMock, open("BAD_ADDRESS", 20)).WillByDefault(Return(false));
因此,任何时候使用参数 "BAD_ADDRESS" 和 20 调用模拟方法 open
时,它将 return 为假,否则为真。
这是一个简单的例子:
using ::testing::_;
using ::testing::AnyNumber;
using ::testing::Return;
class A {
public:
virtual bool bla(int a) = 0;
};
class MOCKA : public A {
public:
MOCK_METHOD1(bla, bool(int));
};
TEST(ABC, aBABA) {
MOCKA a;
ON_CALL(a, bla(_)).WillByDefault(Return(false));
ON_CALL(a, bla(1)).WillByDefault(Return(true));
EXPECT_CALL(a, bla(_)).Times(AnyNumber());
EXPECT_TRUE(a.bla(1));
EXPECT_TRUE(a.bla(1));
EXPECT_TRUE(a.bla(1));
EXPECT_FALSE(a.bla(2));
EXPECT_FALSE(a.bla(3));
EXPECT_FALSE(a.bla(4));
}
编辑 1 我想现在我明白了问题,如果我明白了,那么解决方案就很简单了:
EXPECT_CALL(*connectionMock, open(_, _))
.Times(AnyNumber())
.WillOnce(Return(true))
.WillRepeatedly(Return(false));
当 ConnectionMock::open
在 MySystem::doSth
内部被调用时,它会调用一次 return true
然后总是 return false
无论如何论据是。在这种情况下,您也不需要指定 ON_CALL
。或者您是否确实需要使用 ON_CALL
而不是 EXPECT_CALL
来指定操作?