GMock对象未正确传入异步函数:AWS SDK C++ 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

