news 2026/9/12 16:35:19

如何用 absl::Notification 让 GoogleMock 异步测试确定性地等待后台线程完成

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
如何用 absl::Notification 让 GoogleMock 异步测试确定性地等待后台线程完成

如何用 absl::Notification 让 GoogleMock 异步测试确定性地等待后台线程完成

【免费下载链接】googletestGoogleTest - Google Testing and Mocking Framework项目地址: https://gitcode.com/GitHub_Trending/go/googletest

当你用 GoogleMock(googletest 仓库中的 gMock)测试一个在后台线程执行工作的类时,测试主线程很难知道后台任务何时完成。典型的例子是 gMock Cookbook 中的场景:EventQueue类把所有事件交给一个EventDispatcher接口去分发,而真实实现是在一个后台线程上触发这些事件,导致测试时序难以控制。直接插sleep()语句只是"碰运气",会让测试行为变得不确定。解决方式是:给 mock 的期望加一个调用absl::Notification的 action,让主线程用WaitForNotification()阻塞等待,把异步测试变成同步的、确定性的。

这个模式为什么有效

使用 gMock 与线程 一节给出了两条直接支撑该模式的关键事实:

  • gMock 保证 mock 函数的 action 在调用该 mock 函数的那个线程中执行。因此当后台线程调用DispatchEvent()时,挂在期望上的 action(也就是done.Notify())会在后台线程里运行。
  • 测试代码本身应当只在一个线程中执行;在设置期望(ON_CALL/EXPECT_CALL)和销毁 mock 时,确保没有其他线程正在访问该 mock,否则是未定义行为。gMock 自己负责 mock 调用的锁,不需要你额外加锁。

基于这两点,"后台线程 action 里Notify(),主线程WaitForNotification()" 就构成了一条确定性的等待路径:主线程只有在后台线程真正执行了那个期望时才会被唤醒。

操作步骤

以下代码取自 gMock Cookbook 的 Testing Asynchronous Behavior 小节。示例中的EventQueue对应你的被测类,EventDispatcher对应你为 mock 出来的分发接口,kEventId是对应的入参值;换成你自己的类型后结构不变。前提是测试工程能够使用 Abseil 库(示例用到了absl::Notification)。

第一步,定义 mock 类,用MOCK_METHOD声明要 mock 的方法:

class MockEventDispatcher : public EventDispatcher { MOCK_METHOD(bool, DispatchEvent, (int32), (override)); };

第二步,在测试里先建 mock 和被测对象,然后创建absl::Notification done,并对目标期望挂一个"调用一次就通知"的 lambda action:

TEST(EventQueueTest, EnqueueEventTest) { MockEventDispatcher mock_event_dispatcher; EventQueue event_queue(&mock_event_dispatcher); const int32 kEventId = 321; absl::Notification done; EXPECT_CALL(mock_event_dispatcher, DispatchEvent(kEventId)) .WillOnce([&done] { done.Notify(); }); event_queue.EnqueueEvent(kEventId); done.WaitForNotification(); }

各步骤的作用:

  • absl::Notification done;:声明同步对象,供两个线程共享。
  • EXPECT_CALL(...).WillOnce([&done] { done.Notify(); }):按正常方式设置 gMock 期望,并在期望命中时额外执行一次done.Notify()。这一步必须在触发异步调用之前完成(即测试线程单线程设置期望,后台线程尚未调用 mock)。
  • event_queue.EnqueueEvent(kEventId);:触发被测代码。被测代码随后会在后台线程调用DispatchEvent(kEventId),命中上面的期望并执行done.Notify()
  • done.WaitForNotification();:主线程在此阻塞,直到Notify()被调用后才返回。

如何判断测试写对了

成功条件是文档明确描述的:调用WaitForNotification()后,主线程只有在后台线程执行完那个期望(即异步调用完成)时才会继续,此时"测试套件完成,可以安全退出"。不再需要sleep(),测试结果是确定性的。

文档同时指出一个必须知道的缺点:如果期望没有被满足(例如被测代码根本没调用DispatchEvent(kEventId)),WaitForNotification()会一直等待,测试会一直运行下去,最终虽然会因超时失败,但耗时更长且更难调试。文档给出的缓解办法是把WaitForNotification()换成带超时的版本:

// 期望未被满足时,等待超时后返回,而不是永久挂起 done.WaitForNotificationWithTimeout(ms);

其中ms是等待上限,由你根据后台任务的合理耗时给出。

限制与注意事项

  • 该模式要求 mock 的期望在触发异步调用之前设置完毕,且测试代码只在主线程执行;如果在另一个线程调用 mock 方法的同时设置期望,行为未定义。
  • gMock 不会对不同线程中执行的 action 排序,多个跨线程 action 可能交错执行;如果需要有序,要在 action 内自行加入同步逻辑。
  • 示例只演示了单个期望的一次性通知。多个异步事件可以各建一个Notification,或改用带超时的等待,文档未给出更多变体。

进一步阅读可参考 gMock Cookbook 的 "Testing Asynchronous Behavior" 与 "Using gMock and Threads" 两节。

【免费下载链接】googletestGoogleTest - Google Testing and Mocking Framework项目地址: https://gitcode.com/GitHub_Trending/go/googletest

创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考

版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/9/12 16:33:51

大模型交互新范式:MCP协议原理与实战解析

1. 大模型交互范式演进:从Function Calling到MCP协议 大模型技术发展到今天,交互方式已经历了三次重要迭代。最早的纯文本交互就像对着黑箱说话,开发者无法精确控制模型行为;后来OpenAI提出的Function Calling机制让大模型首次具备…

作者头像 李华
网站建设 2026/9/12 16:29:30

3 个图层实战 deck.gl:跑通百万点地图可视化

3 个图层实战 deck.gl:跑通百万点地图可视化 【免费下载链接】deck.gl WebGL2 powered visualization framework 项目地址: https://gitcode.com/GitHub_Trending/de/deck.gl deck.gl 是一个基于 WebGL2 的可视化框架,专门解决海量地理空间数据在…

作者头像 李华