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

GMock对象未正确传入异步函数:AWS SDK C++ Mock问题求助

解决C++中GMock AWS KMS异步回调Mock对象失效的问题

看起来你在Mock AWS KMS的DecryptOutcome和KMSClient时遇到了异步回调里Mock对象不生效的问题,我之前也踩过类似的坑,给你几个关键的排查和修复方向:

1. 确认基类方法是虚函数,确保Mock能被正确调用

首先检查Aws::KMS::Model::DecryptOutcome中的IsSuccess()是否是虚函数。如果基类的这个方法不是虚的,GMock的EXPECT_CALL根本不会触发——因为调用的是基类的非虚函数,不会走到你的Mock实现里。

如果基类方法不是虚的,你可能需要换一种Mock方式:比如封装一层自己的KMS服务接口,然后Mock这个自定义接口,而不是直接Mock AWS的SDK类(AWS很多SDK类的方法都不是虚的,直接Mock容易踩坑)。

如果IsSuccess()是虚函数,那修正你的MockDecryptOutcome,确保正确覆盖:

class MockDecryptOutcome : public Aws::KMS::Model::DecryptOutcome {
public:
    MOCK_METHOD(bool, IsSuccess, (), (const, override)); // 明确override,确保覆盖基类虚函数
    
    // 如果你还需要MockGetResult()等其他方法,也要同样处理
    MOCK_METHOD(const Aws::KMS::Model::DecryptResult&, GetResult, (), (const, override));
    
    MockDecryptOutcome(const std::string& request) {
        if (request == "SUCCESS") {
            EXPECT_CALL(*this, IsSuccess())
                .WillRepeatedly(testing::Return(true));
            // 这里可以额外Mock GetResult()返回你需要的解密结果
        } else {
            EXPECT_CALL(*this, IsSuccess())
                .WillRepeatedly(testing::Return(false));
        }
    }
};

2. 保证Mock对象的生命周期覆盖异步回调

异步操作的坑在于:当你触发异步Decrypt调用后,测试代码可能已经走到了函数末尾,栈上的MockDecryptOutcome对象被销毁了,等到回调执行时,引用的是一个已销毁的对象,自然不会触发GMock的预期。

解决办法:

  • 把Mock对象放在堆上,用智能指针(比如std::shared_ptr)管理,确保回调执行时对象还活着
  • 在测试用例中,等待异步回调完成后再结束测试(比如用信号量、条件变量同步)

示例代码:

TEST(KmsAsyncTest, DecryptSuccess) {
    // 创建堆上的MockDecryptOutcome,用智能指针持有
    auto mockOutcome = std::make_shared<MockDecryptOutcome>("SUCCESS");
    
    // Mock KMSClient的异步Decrypt方法,手动触发回调
    class MockKMSClient : public Aws::KMS::KMSClient {
    public:
        MOCK_METHOD(void, DecryptAsync, 
            (const Aws::KMS::Model::DecryptRequest& request,
             const Aws::KMS::DecryptResponseReceivedHandler& handler,
             const std::shared_ptr<const Aws::Client::AsyncCallerContext>& context),
            (override));
    };
    
    auto mockClient = std::make_shared<MockKMSClient>();
    
    // 设置Mock预期:当DecryptAsync被调用时,立即触发回调
    EXPECT_CALL(*mockClient, DecryptAsync)
        .WillOnce([mockOutcome](const auto&, const auto& handler, const auto&) {
            // 把Mock的outcome传入回调
            handler(*mockOutcome);
        });
    
    // 触发你的业务代码,调用mockClient->DecryptAsync(...)
    
    // 等待异步回调完成(这里用条件变量示例,根据你的测试框架调整)
    std::promise<void> callbackDone;
    auto future = callbackDone.get_future();
    
    // 假设你的业务代码里的回调会触发这个promise
    // 在实际业务回调中加入:callbackDone.set_value();
    
    // 等待回调完成
    future.wait_for(std::chrono::seconds(1));
    
    // 验证GMock的预期是否都满足
    testing::Mock::VerifyAndClearExpectations(mockClient.get());
    testing::Mock::VerifyAndClearExpectations(mockOutcome.get());
}

3. 避免直接Mock AWS SDK的复杂对象

AWS SDK的很多类设计并不适合直接Mock(比如非虚方法、内部依赖复杂),更稳健的做法是抽象一层自己的KMS服务接口,然后Mock这个抽象接口,而不是直接Mock AWS的KMSClient和DecryptOutcome。

比如:

// 自定义抽象接口
class IKmsService {
public:
    virtual ~IKmsService() = default;
    virtual void DecryptAsync(const std::string& ciphertext, 
                              std::function<void(bool success, const std::string& plaintext)> callback) = 0;
};

// 实际生产实现,封装AWS SDK
class AwsKmsService : public IKmsService {
    // ... 调用AWS KMSClient的逻辑
};

// Mock实现,完全由你控制
class MockKmsService : public IKmsService {
public:
    MOCK_METHOD(void, DecryptAsync, 
        (const std::string& ciphertext, 
         std::function<void(bool success, const std::string& plaintext)> callback),
        (override));
};

这样你的业务代码依赖IKmsService,测试时用MockKmsService,完全不需要关心AWS SDK内部的对象结构,Mock起来简单得多,也避免了异步回调里的对象生命周期问题。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.05.20 11:29:29