GMock - 在预期呼叫后执行操作

GMock - Perform an action AFTER Expected calls

我正在尝试执行单元测试,我需要我的模拟对象在一系列 EXPECT_CALLS 之后执行一个操作,或者作为对其中一个的操作同时允许对 [=23 的模拟调用=] 首先.

这是我的非工作单元测试:

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"
    "[=10=]";

    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);
}

这里的问题是我在 DoAll 中执行的调用是在 returned 模拟方法之前执行的。 return 值需要是消息的大小。

我需要在模拟调用 return 之后执行我的自定义操作。目前,我得到的 return 值为 0 到模拟 sendTextMessage.

测试评论中描述了必须发生的事件顺序。我想假装我正在模拟的套接字收到了相应的消息以响应我的连接请求。有没有办法在模拟调用 returns 后执行操作?

套接字的行为通常是异步的(即,在调用方法后的某个不确定时间发出信号),但是您正在设置模拟对象以使其行为同步(信号是作为调用方法的结果立即发出的) ).您应该尝试模拟异步行为。

通常,您可以通过手动调用信号(而不是作为 invoke 子句的一部分)来实现此行为:

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"
    "[=10=]";

    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);
}

您可能还需要考虑将此测试拆分为两个测试:一个测试客户端是否正确启动新连接,另一个测试客户端是否可以处理来自套接字的响应。