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

测试驱动开发:如何将时间逻辑从代码中解耦

依赖时间触发行为的类的确定性测试方法及时间逻辑外部化实现

一、确定性测试的通用方法

  • 依赖注入:将时间相关的依赖(如定时器、时钟)从类内部构造改为外部传入,测试时可替换为Mock对象,完全控制时间相关行为。
  • 抽象时钟/定时器接口:定义抽象接口封装时间操作,业务类依赖接口而非具体实现,测试时用模拟实现手动控制时间流逝。
  • 时间伪造:在测试环境中替换系统时间的获取逻辑,让时间可以按测试需求推进,不受真实系统时间或负载影响。
  • 状态驱动验证:不等待真实时间,通过监听类的状态变化、接口调用记录、回调触发次数等方式,直接验证业务逻辑的正确性。

二、将时间逻辑从PassiveBuzzer类外部化的具体实现

1. 定义定时器抽象接口

先封装定时器的核心操作成抽象接口,让现有timerfd包装类实现该接口:

class TimerInterface {
public:
    virtual ~TimerInterface() = default;
    virtual void start(std::chrono::nanoseconds period) = 0;
    virtual void stop() = 0;
    // 按需添加修改周期、获取运行状态等方法
};

// 原有timerfd包装类适配接口
class TimerFD : public TimerInterface {
public:
    void start(std::chrono::nanoseconds period) override {
        // 原有timerfd启动逻辑
    }
    void stop() override {
        // 原有timerfd停止逻辑
    }
};

2. 修改PassiveBuzzer构造函数

将定时器依赖改为外部传入,取消内部构造逻辑:

class PassiveBuzzer {
private:
    std::unique_ptr<TimerInterface> timer_;
    PWM::Line& pwm_line_;
    unsigned int on_frequency_;

public:
    PassiveBuzzer(std::unique_ptr<TimerInterface> timer, PWM::Line& pwm_line, unsigned int on_frequency)
        : timer_(std::move(timer)), pwm_line_(pwm_line), on_frequency_(on_frequency) {}

    // 原有业务方法示例
    void setDutyCycle(float duty) {
        // 业务逻辑中按需调用timer_->start(...)等接口
    }
};

3. 实现测试用Mock定时器

在单元测试中编写Mock定时器,记录调用行为并支持手动触发回调:

class MockTimer : public TimerInterface {
public:
    int start_call_count = 0;
    std::chrono::nanoseconds last_set_period;
    bool is_running = false;
    std::function<void()> timer_callback; // 存储定时器到期后的回调

    void start(std::chrono::nanoseconds period) override {
        start_call_count++;
        last_set_period = period;
        is_running = true;
    }

    void stop() override {
        is_running = false;
    }

    // 手动触发定时器到期逻辑
    void trigger_expiry() {
        if (timer_callback) {
            timer_callback();
        }
    }
};

4. 执行确定性测试

通过Mock定时器验证PassiveBuzzer的行为:

TEST(PassiveBuzzerTest, CorrectlyConfiguresTimer) {
    MockTimer mock_timer;
    MockPWMLine mock_pwm; // 你已有的Mock PWM线路实现
    PassiveBuzzer buzzer(std::make_unique<MockTimer>(mock_timer), mock_pwm, 440);

    buzzer.setDutyCycle(0.5f);

    // 验证定时器启动参数是否正确
    EXPECT_EQ(mock_timer.start_call_count, 1);
    EXPECT_EQ(mock_timer.last_set_period, std::chrono::nanoseconds(1000000000 / 440));
    EXPECT_TRUE(mock_timer.is_running);

    // 手动触发定时器到期,验证蜂鸣器状态切换逻辑
    mock_timer.trigger_expiry();
    EXPECT_TRUE(mock_pwm.is_toggled()); // 假设MockPWMLine提供状态检查方法
}

三、额外优化建议

  • 抽象时钟接口:如果类中需要获取当前时间,同样封装ClockInterface,测试时用MockClock返回预设时间。
  • 解耦时间与业务逻辑:让PassiveBuzzer仅负责蜂鸣器的PWM控制,时间调度逻辑完全交给外部定时器,降低类的职责复杂度。
  • 借助测试框架工具:使用Google Test的GMock等工具,可以更高效地创建Mock对象、验证接口调用顺序和参数。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.06.24 10:12:46