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

GMock技术问询:如何实现任意顺序块内的调用序列约束

如何用GMock实现支持调用对顺序调整的成对约束测试?

业务代码说明

给定接口定义:

class Interface
{
public:
    virtual ~Interface = default;

    virtual void open(int id) = 0;
    virtual void close() = 0;

protected:
    // 默认其他特殊成员
};

核心规则:

  • close()只能在open()之后调用
  • 下一次调用open()前必须先调用close()(同一时间只能有一个打开的ID)
  • open()的ID不可重复

合法实现要求每个ID的open()/close()成对调用,且成对的顺序可调整(比如先处理ID2的成对,再处理ID1的);以下错误实现因违反规则需被识别:

void actWrong(Interface & interface)
{
    interface.open(1);
    interface.open(2);
    interface.open(3);
    interface.close();
    interface.close();
    interface.close();
}

测试需求

  1. 调整open()/close()调用对的顺序时,测试仍通过
  2. 成对规则被破坏(连续调用open、连续调用close、重复ID等)时,测试失败

现有尝试的问题

  • 使用InSequence:能满足成对约束,但无法支持调用对的顺序调整
  • 使用Sequence:支持顺序调整,但无法识别成对规则的破坏
  • 状态跟踪方式:需重复实现业务逻辑,扩展性差

解决方案

借助GMock的InSequence全局序列约束,结合Unique参数匹配器,可以同时满足顺序调整支持和规则校验的需求:

1. 定义Mock类

class MockInterface : public Interface {
public:
    MOCK_METHOD(void, open, (int id), (override));
    MOCK_METHOD(void, close, (), (override));
};

2. 编写测试用例

合法调用场景(支持顺序调整)

TEST(InterfaceTest, ValidCallPairsWithAdjustedOrder) {
    MockInterface mock;
    Sequence global_seq;

    // 全局序列强制调用顺序为:open -> close -> open -> close(可扩展到更多成对)
    EXPECT_CALL(mock, open(_))
        .InSequence(global_seq)
        .Times(2)
        .With(Args<0>(Unique())); // 确保ID唯一

    EXPECT_CALL(mock, close())
        .InSequence(global_seq)
        .Times(2);

    // 测试合法顺序:先处理ID2的成对,再处理ID1的
    mock.open(2);
    mock.close();
    mock.open(1);
    mock.close();
}

非法调用场景(连续open)

TEST(InterfaceTest, InvalidConsecutiveOpens) {
    MockInterface mock;
    Sequence global_seq;

    EXPECT_CALL(mock, open(_))
        .InSequence(global_seq)
        .Times(2)
        .With(Args<0>(Unique()));

    EXPECT_CALL(mock, close())
        .InSequence(global_seq)
        .Times(2);

    // 执行错误调用:连续open两个ID,违反全局序列约束
    mock.open(1);
    mock.open(2); // 此处触发测试失败
}

非法调用场景(重复ID)

TEST(InterfaceTest, InvalidDuplicateID) {
    MockInterface mock;
    Sequence global_seq;

    EXPECT_CALL(mock, open(_))
        .InSequence(global_seq)
        .Times(2)
        .With(Args<0>(Unique()));

    EXPECT_CALL(mock, close())
        .InSequence(global_seq)
        .Times(2);

    // 执行错误调用:重复使用ID1
    mock.open(1);
    mock.close();
    mock.open(1); // 此处触发测试失败
}

方案说明

  • InSequence(global_seq):强制全局调用顺序必须是open与close交替,直接约束了“下一次open前必须调用close”以及“close只能在open之后调用”的规则,同时允许调整成对的顺序(只要整体是交替的)。
  • Args<0>(Unique()):利用GMock原生匹配器确保所有open的ID不重复,无需手动维护状态集合。
  • Times(n):指定成对的数量,可根据测试场景灵活调整(比如测试3个成对调用时,将Times改为3即可)。

这种方案完全基于GMock原生功能,避免了重复实现业务逻辑,同时满足所有测试需求。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.06.21 22:43:10