I would like to test the method of my system, whose return value partially depends on the return value of the call to some kind of connection interface. In most cases I would like the IConnection
to return true
upon any kind of call to it's open(_, _)
method. Except in one case, when I explicitly test for the condition with failed connection.
Example:
/*
* 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());
}
The problems with my current solution is that when the EXPECT_CALL
override becomes saturated, even though gmock goes back to the default action specified on ON_CALL
, every subsequent call to open(_, _)
is causing the following warning:
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:
even though I'm using NiceMock
. I can get rid of the warning by specifying EXPECT_CALL
with WillRepeatedly(Return(true))
, but this is the duplication of my code in ON_CALL
.
I would like to know, how can I override the default action specified with ON_CALL
for just one call to IConnection::open
, and then go back to the defaults, without causing gmock to print a warning. The perfect solution would be something similar to:
EXPECT_CALL(*connectionMock, open(_, _)).WillOnce(Return(false)).DisableExpectationAfterSaturation();
but it doesn't exist. RetiresOnSaturation
doesn't work as I would like, because it fails the test after getting saturated (doesn't match action specified with ON_CALL
).
If you are also in the habit of giving tests descriptive names that tell what they verify, you can often easily guess what's wrong just from the test log itself. So use ON_CALL by default, and only use EXPECT_CALL when you actually intend to verify that the call is made.
EXPECT_CALL not only defines the behavior, but also sets an expectation that the method will be called with the given arguments, for the given number of times (and in the given order when you specify the order too).
EDIT 2
The DoDefault()
- feature comes close to what is asked in the question. It specifies that an action in EXPECT_CALL
should go back to the default action specified by 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());
Initial answer
If the return value of IConnection::open
depends on the parameters you can specify ON_CALL
twice but with different arguments (or rather arguments instead of the placeholder):
ON_CALL(*connectionMock, open(_, _)).WillByDefault(Return(true));
ON_CALL(*connectionMock, open("BAD_ADDRESS", 20)).WillByDefault(Return(false));
So any time the mocked method open
will be called with arguments "BAD_ADDRESS" and 20, it will return false, and true otherwise.
Here is a simple example:
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));
}
EDIT 1 I think now I understood the problem and if I did then the solution is very simple:
EXPECT_CALL(*connectionMock, open(_, _))
.Times(AnyNumber())
.WillOnce(Return(true))
.WillRepeatedly(Return(false));
When ConnectionMock::open
will be called inside of MySystem::doSth
it will once return true
and then always return false
no matter what the arguments are. In this case you also don't need to specify ON_CALL
. Or do you definitely need to specify the actions with ON_CALL
instead of EXPECT_CALL
?
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With