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

TDD开发中如何测试动作执行顺序?求通用解决方案

测试有执行顺序要求的TDD代码通用方案

在TDD开发需要按特定顺序执行动作的代码时,常遇到测试无法区分正确顺序与错误顺序实现的问题。比如发送AT命令的exchange函数,要求先发送命令,再读取响应,但最初的测试无法阻止“先读响应再发命令”的错误实现通过。

问题示例

最初的测试代码:

def test_exchange_sends_a_command_and_returns_the_received_response():
    stdout = io.StringIO()
    stdin = io.StringIO("CONNECT\r\n")

    response = exchange(stdout, stdin, "ATD")

    assert stdout.getvalue() == "ATD\r\n"
    assert response == "CONNECT"

正确实现:

def exchange(stdout, stdin, command):
    stdout.write(command + "\r\n")
    return stdin.readline().strip()

错误实现(仍能通过测试):

def exchange(stdout, stdin, command):
    response = stdin.readline().strip()
    stdout.write(command + "\r\n")
    return response

通用测试模式

1. 带序列验证的自定义测试替身

实现能记录调用顺序的标准输入输出替身,测试时不仅验证最终状态,还检查动作的执行顺序:

class OrderedStdoutStub:
    def __init__(self):
        self.written_content = []
        self.interactions = []

    def write(self, buf):
        self.written_content.append(buf)
        self.interactions.append(("write", buf))

class OrderedStdinStub:
    def __init__(self, response):
        self.response = response
        self.interactions = []

    def readline(self):
        self.interactions.append(("readline",))
        return self.response

def test_exchange_correct_order():
    stdout = OrderedStdoutStub()
    stdin = OrderedStdinStub("CONNECT\r\n")

    response = exchange(stdout, stdin, "ATD")

    # 验证最终结果
    assert stdout.written_content == ["ATD\r\n"]
    assert response == "CONNECT"
    # 验证执行顺序:先发送命令,再读取响应
    assert stdout.interactions[0] == ("write", "ATD\r\n")
    assert stdin.interactions[0] == ("readline",)
    # 确保write动作早于readline
    all_interactions = stdout.interactions + stdin.interactions
    assert all_interactions.index(("write", "ATD\r\n")) < all_interactions.index(("readline",))

2. 利用Mock框架做行为验证

借助语言自带的Mock工具(如Python的unittest.mock),直接验证方法的调用顺序:

from unittest.mock import Mock, call

def test_exchange_with_mock_validation():
    stdout = Mock()
    stdin = Mock()
    stdin.readline.return_value = "CONNECT\r\n"

    response = exchange(stdout, stdin, "ATD")

    # 验证调用内容和顺序
    stdout.assert_has_calls([call.write("ATD\r\n")])
    stdin.assert_has_calls([call.readline()])
    # 严格验证全局调用顺序:write先于readline
    all_calls = stdout.method_calls + stdin.method_calls
    assert all_calls == [call.write("ATD\r\n"), call.readline()]
    assert response == "CONNECT"

3. 基于“间谍”模式的交互记录

这种模式下,测试替身会完整记录所有交互行为,测试时直接检查交互的时间线,比状态验证更精准。上面的Mock方案本质就是间谍模式的实现。

核心思路

之前的测试仅验证最终状态(输出内容、返回值),未验证执行顺序。上述方案通过记录动作序列,确保代码严格按照业务要求的顺序执行,彻底排除错误顺序的实现。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.06.12 07:44:52