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

如何在首次出现未预期的gmock调用时终止gtest测试

问题:是否存在与ASSERT_*等效的EXPECT_CALL机制,可在首次不匹配调用时终止测试?

在使用gtest+gmock测试硬件寄存器编程序列时,所有EXPECT_CALL均基于StrictMock和InSequence。一旦出现调用缺失或错误,后续调用大概率会因序列错位产生大量冗余错误信息,难以定位根本故障。

示例代码

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

void hw_register_write(uint32_t, uint32_t) {}

class hw_register_access {
public:
    virtual void write(uint32_t address, uint32_t value) = 0;
};

class hw_registers : public hw_register_access {
public:
    void write(uint32_t address, uint32_t value) override {
        return hw_register_write(address, value);
    }
};

class mock_registers : public hw_register_access {
public:
    MOCK_METHOD(void, write, (uint32_t address, uint32_t value), (override));
};

void program_foo(hw_register_access& reg) {
    reg.write(0x10, 0x123);
    reg.write(0x10, 0x456);  // 此处存在错误,希望测试立即终止
    for (size_t i = 0; i < 10; i++) {
        reg.write(0x20, 0x789);  // 不希望此处产生冗余错误信息
    }
}

TEST(programming_sequence_test, program_foo) {
    testing::StrictMock<mock_registers> mock{};
    testing::InSequence seq{};

    EXPECT_CALL(mock, write(0x10, 0x123));
    EXPECT_CALL(mock, write(0x11, 0x456));
    EXPECT_CALL(mock, write(0x20, 0x789)).Times(10);

    program_foo(mock);
}

现有问题

当出现不匹配调用时,测试会继续执行后续代码,生成大量冗余错误(如示例中会产生300多行输出),掩盖了根本问题:

Unexpected mock function call - returning directly.
Function call: write(16, 1110)
...
Expected arg #0: is equal to 17
Actual: 16

解决方案

gmock没有直接提供ASSERT_CALL宏,但可以通过以下两种方式实现首次失败即终止测试:

方法1:启用失败时抛出异常

通过设置testing::GTEST_FLAG(throw_on_failure)为true,让gtest/gmock在检测到第一个失败时抛出异常,立即终止测试执行,避免后续冗余错误。

修改后的测试代码:

TEST(programming_sequence_test, program_foo) {
    // 开启失败即抛出异常
    testing::GTEST_FLAG(throw_on_failure) = true;
    
    testing::StrictMock<mock_registers> mock{};
    testing::InSequence seq{};

    EXPECT_CALL(mock, write(0x10, 0x123));
    EXPECT_CALL(mock, write(0x11, 0x456));
    EXPECT_CALL(mock, write(0x20, 0x789)).Times(10);

    program_foo(mock);
    
    // 可选:恢复全局设置,避免影响其他测试用例
    testing::GTEST_FLAG(throw_on_failure) = false;
}

方法2:自定义测试监听器(进阶)

如果需要更精细的控制,可以自定义gtest监听器,在检测到第一个失败时终止测试执行。这种方式适合全局统一配置的场景:

class FailFastListener : public testing::EmptyTestEventListener {
public:
    void OnTestPartResult(const testing::TestPartResult& result) override {
        if (result.failed()) {
            // 终止所有测试执行
            testing::UnitTest::GetInstance()->Abort();
        }
    }
};

TEST(programming_sequence_test, program_foo) {
    static bool listener_added = false;
    if (!listener_added) {
        testing::UnitTest::GetInstance()->listeners().Append(new FailFastListener());
        listener_added = true;
    }
    
    testing::StrictMock<mock_registers> mock{};
    testing::InSequence seq{};

    EXPECT_CALL(mock, write(0x10, 0x123));
    EXPECT_CALL(mock, write(0x11, 0x456));
    EXPECT_CALL(mock, write(0x20, 0x789)).Times(10);

    program_foo(mock);
}

总结

推荐优先使用方法1,实现简单且能快速解决冗余错误问题。如果需要全局生效的失败终止策略,可以选择方法2。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.06.13 17:45:14