Reputation: 4010
I am trying to perform a unit test where I need my mock object to perform an action AFTER a sequence of EXPECT_CALLS, or as an action on one of them while allowing the mocked call to return first.
Here is my non working unit test:
class MockWebSocket : public alert::QWebSocketInterface
{
public:
MOCK_METHOD(void, open, (QUrl const&), (override));
MOCK_METHOD(void, close, (QWebSocketProtocol::CloseCode, QString const&), (override));
MOCK_METHOD(qint64, sendTextMessage, (QString const&), (override));
MOCK_METHOD(QAbstractSocket::SocketState, state, (), (const, override));
MOCK_METHOD(QString, origin, (), (const, override));
MOCK_METHOD(QString, errorString, (), (const, override));
};
/*!
* @brief Defines a custom action, where we can invoke methods that were unrelated to a mock object's "expected call"
*/
ACTION_P3(InvokeUnrelatedMethodWith1Arg, classPointer, pointerToMemberFunc, first)
{
(classPointer->*pointerToMemberFunc)(first);
return 0; // This is being used as a return value from the mocked call. I want this action done AFTER the return instead!
};
/*
* @brief Test - Test if the state of the client is 'connected' after the CONNECTED frame is received
* Procedure:
* Call connect
* emit connected signal
* Send the CONNECT stomp frame
* Have the mock socket send back a CONNECTED stomp frame
* Check state
* Expected Results:
* State is CONNECTED
*/
TEST(ClientTests, ConnectedStateAfterConnectedFrame)
{
QByteArray connectedFrame =
"CONNECTED\n"
"version:1.2\n"
"session:714e58fb\n"
"server:ActiveMQ-Artemis/2.14.0\n"
"heart-beat:0,30000\n\n"
"\0";
auto mockWebsocket = std::make_shared<MockWebSocket>();
{
InSequence sequence;
EXPECT_CALL(*mockWebsocket, close(_, _)).Times(1);
EXPECT_CALL(*mockWebsocket, open(_))
.Times(1)
.WillOnce(InvokeWithoutArgs(mockWebsocket.get(), &MockWebSocket::connected));
EXPECT_CALL(*mockWebsocket, sendTextMessage(_))
.Times(1)
.WillOnce(DoAll(
Invoke([](QString message) {return message.size();}),
InvokeUnrelatedMethodWith1Arg(mockWebsocket.get(), &MockWebSocket::binaryMessageReceived,connectedFrame)
));
// Close is called during cleanup, so we have to allow more calls explicitly
EXPECT_CALL(*mockWebsocket, close(_, _)).Times(AtLeast(1));
}
auto client = std::make_unique<StompClientSideConnection>(mockWebsocket);
client->connect(QUrl("ws://localhost:5000"));
EXPECT_EQ(client->getState(), StompClientSideConnection::State::CONNECTING);
}
Problem here is that the invoke I did within the DoAll is performed before the mocked method is returned. The return value need to be the size of the message.
I need my custom action performed AFTER the mocked call returns. Currently, I am getting a return value of 0 to the mocked sendTextMessage
.
The sequence of events that has to occur is described in the test comments. I want to pretend the socket I am mocking got the appropriate message in response to my connection request. Is there a way to perform an action after the mocked call returns?
Upvotes: 0
Views: 1065
Reputation: 7777
A socket typically behaves asynchronously (i.e., signals are emitted at some indeterminate time after calling methods), but you are setting up the mock object such that it behaves synchronously (signals are emitted immediately as a result of calling the method). You should be attempting to simulate asynchronous behavior.
Typically, you would achieve this behavior by calling the signal manually (and not as part of an invoke clause):
TEST(ClientTests, ConnectedStateAfterConnectedFrame)
{
QByteArray connectedFrame =
"CONNECTED\n"
"version:1.2\n"
"session:714e58fb\n"
"server:ActiveMQ-Artemis/2.14.0\n"
"heart-beat:0,30000\n\n"
"\0";
auto mockWebsocket = std::make_shared<MockWebSocket>();
{
InSequence sequence;
EXPECT_CALL(*mockWebsocket, close(_, _)).Times(1);
EXPECT_CALL(*mockWebsocket, open(_))
.Times(1)
.WillOnce(InvokeWithoutArgs(mockWebsocket.get(), &MockWebSocket::connected));
EXPECT_CALL(*mockWebsocket, sendTextMessage(_))
.Times(1)
.WillOnce(DoAll(
Invoke([](QString message) {return message.size();}),
));
// Close is called during cleanup, so we have to allow more calls explicitly
EXPECT_CALL(*mockWebsocket, close(_, _)).Times(AtLeast(1));
}
auto client = std::make_unique<StompClientSideConnection>(mockWebsocket);
client->connect(QUrl("ws://localhost:5000"));
emit mockWebsocket->binaryMessageReceived(connectedFrame);
EXPECT_EQ(client->getState(), StompClientSideConnection::State::CONNECTING);
}
You may also want to consider splitting this test up into two tests: one to test that the client initiates a new connection correctly, and another to test that the client can handle the response from the socket.
Upvotes: 1