You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何用Gmock实现可手动控制阻塞状态的Mock函数?

How to Implement a Blocking Mock Function with GMock

Got it, let's tackle how to implement a blocking mock function with GMock for your scenario—this is a common use case when testing threaded code that relies on blocking I/O functions like your Read method. The goal is to make the mock Read block by default, and let you manually trigger it to return a value (either success or failure) whenever you need during testing.

Core Approach

GMock doesn't have built-in blocking functionality, but we can combine C++ synchronization primitives (mutex + condition variable) with GMock's custom actions (Invoke) to build this behavior. Here's a step-by-step solution tailored to your code:


Step 1: Enhance Your Mock Class with Sync Primitives

First, update your BlockFuncs mock to include variables that control blocking, store the return value, and hold the message to output:

#include <mutex>
#include <condition_variable>

class BlockFuncs: public BlockFuncsIf {
public:
    MOCK_METHOD1(Write, bool(const Msg&));
    MOCK_METHOD1(Read, bool(Msg* msg));

    // Synchronization primitives for blocking control
    std::mutex mtx;
    std::condition_variable cv;
    bool allow_read = false;       // Flag to unblock Read
    bool read_return_value = false;// Value Read will return
    Msg output_msg;                // Message Read will write to the output pointer
};

Step 2: Define a Blocking Custom Action for Read

Create a helper function that implements the blocking logic. This function will be bound to the mock Read method using GMock's Invoke:

// Custom action that makes Read block until we trigger it
bool BlockingReadAction(BlockFuncs* mock, Msg* msg) {
    std::unique_lock<std::mutex> lock(mock->mtx);
    
    // Wait until we explicitly allow Read to return
    mock->cv.wait(lock, [mock]() { return mock->allow_read; });

    // Copy the pre-configured message to the output pointer
    if (msg != nullptr) {
        *msg = mock->output_msg;
    }

    // Save the return value and reset the block flag for next calls
    bool result = mock->read_return_value;
    mock->allow_read = false;

    return result;
}

Step 3: Set Up the Mock in Your Test Case

In your test, bind the custom action to the mock Read method, then control the blocking behavior by triggering the condition variable:

#include <thread>
#include <gtest/gtest.h>
#include <gmock/gmock.h>

TEST(TestedClassTest, ManualControlOfBlockingRead) {
    BlockFuncs mock_block_funcs;
    TestedClass tested_object(&mock_block_funcs);

    // Bind the blocking action to the mock Read method
    ON_CALL(mock_block_funcs, Read(_))
        .WillByDefault(testing::Invoke(&mock_block_funcs, BlockingReadAction));

    // Start the thread that runs expectRead (which loops on Read)
    std::thread read_thread(&TestedClass::expectRead, &tested_object);

    // --------------------------
    // First manual trigger: make Read return true (keep the loop running)
    // --------------------------
    {
        std::lock_guard<std::mutex> lock(mock_block_funcs.mtx);
        mock_block_funcs.output_msg = Msg{/* Initialize your test message here */};
        mock_block_funcs.read_return_value = true;
        mock_block_funcs.allow_read = true;
    }
    mock_block_funcs.cv.notify_one(); // Wake up the blocked Read call

    // Wait for the TestedClass to process the Read (using its condition variable)
    std::unique_lock<std::mutex> tested_lock(tested_object.mtx); // Assume TestedClass has a mutex for its cv
    tested_object.cv.wait(tested_lock); // Wait until expectRead calls cv.notify_all()

    // --------------------------
    // Second manual trigger: make Read return false (exit the loop)
    // --------------------------
    {
        std::lock_guard<std::mutex> lock(mock_block_funcs.mtx);
        mock_block_funcs.read_return_value = false;
        mock_block_funcs.allow_read = true;
    }
    mock_block_funcs.cv.notify_one();

    // Wait for the read thread to finish
    read_thread.join();

    // Verify expectations (e.g., Read was called exactly twice)
    EXPECT_CALL(mock_block_funcs, Read(_))
        .Times(2);
}

Step 4: (Optional) Simplify Triggering with a Helper Function

To clean up your test code, wrap the trigger logic in a helper function:

void TriggerMockRead(BlockFuncs& mock, bool return_value, const Msg& msg = {}) {
    std::lock_guard<std::mutex> lock(mock.mtx);
    mock.output_msg = msg;
    mock.read_return_value = return_value;
    mock.allow_read = true;
    mock.cv.notify_one();
}

Then in your test, you can replace the trigger blocks with:

// Trigger Read to return true with a test message
TriggerMockRead(mock_block_funcs, true, test_msg);
tested_object.cv.wait(tested_lock);

// Trigger Read to return false and exit the loop
TriggerMockRead(mock_block_funcs, false);

Key Notes

  • Avoid Deadlocks: Always ensure you lock the mutex before modifying the control flags (allow_read, read_return_value), and never hold the mutex while waiting on a condition variable (use std::unique_lock for that).
  • Reset Flags: We reset allow_read to false after each trigger so subsequent Read calls will block again automatically.
  • Sync with TestedClass: Use the TestedClass's condition variable to wait for the Read operation to complete, ensuring your test steps execute in the correct order.

内容的提问来源于stack exchange,提问作者lior.i

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.05.11 07:27:28