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

如何编写Google Mock匹配器以匹配void指针参数?

GMock匹配const void*参数的解决方案

问题描述

需要Mock一个接收const void*类型参数的函数,希望基于传入指针指向的值编写匹配器,但直接指针比较无效(因为调用时传递的是循环变量的地址,和测试代码中的变量地址不同)。尝试使用SaveArgPointee时报错const void* is not a pointer-to-oject type,自定义Action也因类型转换问题无法实现。

示例代码

#include <vector>
#include "gmock/gmock.h"
#include "gtest/gtest.h"

class A
{
public:
    A(std::vector<uint32_t> data) : m_data(data) {}
    void doSomething()
    {
        // 注意:原代码中这里用了data,应该改为m_data
        for (auto x : m_data)
            writeData(&x, sizeof(x));
    }
    void writeData(const void* val, size_t size)
    {
        // 实际逻辑更复杂
        std::cout << "Writing data" << std::endl;
    }
private:
    std::vector<uint32_t> m_data;
};

class MockA : public A
{
public: 
    MOCK_METHOD(void, writeData, (const void* val, size_t size));
}; // 原代码缺少分号

TEST(TestA, writeData)
{
    std::vector<uint32_t> testData{1,2,3,4};
    MockA myMockObj(testData);
    
    uint32_t expVal1 = 1;
    uint32_t expVal2 = 2;
    uint32_t expVal3 = 3;
    uint32_t expVal4 = 4;
    
    // 问题:writeData接收const void*,指针比较无效,因为地址不同
    // 例如&testData[1] != m_data[1],(void*)&expVal2 != val
    
    EXPECT_CALL(myMockObj, writeData(expVal4, 4)).Times(1);
    EXPECT_CALL(myMockObj, writeData(expVal3, 4)).Times(1);
    EXPECT_CALL(myMockObj, writeData(expVal2, 4)).Times(1);
    EXPECT_CALL(myMockObj, writeData(expVal1, 4)).Times(1);
    
    myMockObj.doSomething();
}

解决方案

核心是通过自定义GMock匹配器,将const void*安全转换为目标类型后比较指向的值,同时验证size参数的正确性。

方案1:自定义双参数匹配器(同时验证指针值和size)

使用MATCHER_P2宏创建匹配器,同时接收预期值和预期size,打包两个参数进行匹配:

// 自定义匹配器:验证const void*指向的uint32_t值,以及size参数
MATCHER_P2(MatchVoidPtrToUint32, expectedVal, expectedSize, "") {
    // 先校验size是否符合预期,同时确保size是uint32_t的大小,避免错误转换
    if (arg.second != expectedSize || expectedSize != sizeof(uint32_t)) {
        return false;
    }
    // 将void*转换为uint32_t指针,取值比较
    const uint32_t* actualVal = static_cast<const uint32_t*>(arg.first);
    return *actualVal == expectedVal;
}

修改测试中的EXPECT_CALL,用_匹配任意参数,然后通过WillOnce指定匹配逻辑:

TEST(TestA, writeData)
{
    std::vector<uint32_t> testData{1,2,3,4};
    MockA myMockObj(testData);
    
    uint32_t expVal1 = 1;
    uint32_t expVal2 = 2;
    uint32_t expVal3 = 3;
    uint32_t expVal4 = 4;
    const size_t uint32Size = sizeof(uint32_t);

    EXPECT_CALL(myMockObj, writeData(_, _))
        .WillOnce(MatchVoidPtrToUint32(expVal1, uint32Size))
        .WillOnce(MatchVoidPtrToUint32(expVal2, uint32Size))
        .WillOnce(MatchVoidPtrToUint32(expVal3, uint32Size))
        .WillOnce(MatchVoidPtrToUint32(expVal4, uint32Size));
    
    myMockObj.doSomething();
}

方案2:单参数匹配器组合(分别验证每个参数)

先创建仅验证const void*指向值的匹配器,再和size的精确匹配组合使用:

// 自定义匹配器:验证const void*指向的uint32_t值
MATCHER_P(MatchVoidPtrToUint32Val, expectedVal, "") {
    // 这里默认调用方已经保证size正确,或者可以在外部配合size匹配器
    const uint32_t* actualVal = static_cast<const uint32_t*>(arg);
    return *actualVal == expectedVal;
}

修改EXPECT_CALL,组合两个匹配器:

TEST(TestA, writeData)
{
    std::vector<uint32_t> testData{1,2,3,4};
    MockA myMockObj(testData);
    
    uint32_t expVal1 = 1;
    uint32_t expVal2 = 2;
    uint32_t expVal3 = 3;
    uint32_t expVal4 = 4;
    const size_t uint32Size = sizeof(uint32_t);

    EXPECT_CALL(myMockObj, writeData(MatchVoidPtrToUint32Val(expVal1), uint32Size)).Times(1);
    EXPECT_CALL(myMockObj, writeData(MatchVoidPtrToUint32Val(expVal2), uint32Size)).Times(1);
    EXPECT_CALL(myMockObj, writeData(MatchVoidPtrToUint32Val(expVal3), uint32Size)).Times(1);
    EXPECT_CALL(myMockObj, writeData(MatchVoidPtrToUint32Val(expVal4), uint32Size)).Times(1);
    
    myMockObj.doSomething();
}

注意事项

  1. 确保const void*指向的确实是目标类型(这里是uint32_t),否则类型转换会导致未定义行为,建议在匹配器中加入size校验(如方案1)。
  2. 原示例代码中的两处编译错误需修正:doSomething中遍历的是m_data而非构造函数参数data,MockA类定义末尾缺少分号。

内容的提问来源于stack exchange,提问作者ventsyv

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.06.28 09:05:39