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

如何在PHPUnit的文本断言中允许已知位置的预期差异(Golden Master测试场景)

如何在PHPUnit的文本断言中允许已知位置的预期差异(Golden Master测试场景)

我太懂这种头疼的情况了——搞Golden Master测试本来就是要抓精准的文本匹配,但那些暂时没法根除的副作用(比如固定位置的动态数字、临时ID这类)总在同一个地方搞破坏,直接用$this->assertSame($expected, $actual)每次都红,完全没法正常跑测试。

给你几个实用的解决方案,都是我自己在项目里用过的,完全适配你的场景:

方法一:正则标准化可变内容后再断言

最直接的思路:把预期和实际文本里的可变部分都替换成统一的占位符,然后再做严格相等断言。这样既忽略了允许的差异,又能在其他不允许修改的地方出现变化时立刻失败。

针对你的例子,可变的是DIFF后面的数字,还有containing an后面的数字,我们可以写个简单的清理函数:

// 定义标准化函数,把已知的可变内容替换成统一标记
function normalizeGoldenText(string $text): string {
    // 替换"DIFF 数字"为"DIFF XX"
    $text = preg_replace('/DIFF \d+/', 'DIFF XX', $text);
    // 替换"containing an 数字 expected diff"为"containing an XX expected diff"
    $text = preg_replace('/containing an \d+ expected diff/', 'containing an XX expected diff', $text);
    return $text;
}

// 测试里的断言改成这样
$this->assertSame(normalizeGoldenText($expected), normalizeGoldenText($actual));

完全适配你的场景:

  • 第一个测试用例里,两个文本的可变数字被替换成XX后完全一致,断言通过;
  • 第二个失败案例里,“Also this SHOULD be the same but is not”这一行没被标准化处理,替换后还是和预期不一样,断言直接失败,正好是我们要的效果。

方法二:封装成自定义PHPUnit断言(适合高频使用场景)

如果你的项目里很多测试都要处理这种Golden Master差异,不如把逻辑封装成自定义断言,写在测试基类里,用起来更简洁:

// 在你的测试基类(比如BaseTestCase)里添加这个方法
protected function assertGoldenMasterMatch(string $expected, string $actual): void {
    $normalize = function(string $text) {
        $text = preg_replace('/DIFF \d+/', 'DIFF XX', $text);
        $text = preg_replace('/containing an \d+ expected diff/', 'containing an XX expected diff', $text);
        return $text;
    };

    $normalizedExpected = $normalize($expected);
    $normalizedActual = $normalize($actual);

    // 自定义错误信息,方便定位问题
    $this->assertSame(
        $normalizedExpected, 
        $normalizedActual, 
        "Golden Master不匹配:已忽略允许的差异,但仍存在未预期的文本变化"
    );
}

// 之后在测试里直接调用就行
$this->assertGoldenMasterMatch($expected, $actual);

这样每次用的时候不用重复写正则,代码更干净,错误信息也更友好。

方法三:行级精准校验(适合复杂可变场景)

如果可变部分的格式有规则,但位置固定,你也可以把文本按行拆分,对每一行做针对性校验:允许固定行的格式符合规则,其他行必须完全一致。

比如你的例子里,第2行和第6行是可变行,其他行必须严格匹配:

$expectedLines = explode("\n", trim($expected));
$actualLines = explode("\n", trim($actual));

// 先检查行数是否一致,避免少行/多行的情况
$this->assertCount(count($expectedLines), $actualLines, "Golden Master文本行数不匹配");

foreach ($expectedLines as $lineIndex => $expectedLine) {
    $actualLine = $actualLines[$lineIndex];
    // 第2行(索引1,从0开始计数)和第6行(索引5)是允许可变的行
    if ($lineIndex === 1 || $lineIndex === 5) {
        if ($lineIndex === 1) {
            // 校验行格式:必须是"Here might be an expected DIFF 数字"
            $this->assertMatchesRegularExpression(
                '/^Here might be an expected DIFF \d+$/', 
                $actualLine, 
                "第{$lineIndex}行格式不符合预期"
            );
        } else {
            // 校验第6行格式:必须是"Yet another line containing an 数字 expected diff"
            $this->assertMatchesRegularExpression(
                '/^Yet another line containing an \d+ expected diff$/', 
                $actualLine, 
                "第{$lineIndex}行格式不符合预期"
            );
        }
    } else {
        // 其他行必须完全一致
        $this->assertSame(
            $expectedLine, 
            $actualLine, 
            "第{$lineIndex}行与Golden Master不匹配"
        );
    }
}

这种方法的好处是:不仅忽略了允许的数字差异,还能确保可变行的格式完全符合预期(比如不能突然变成字母),适合对可变内容有格式要求的场景。

我自己平时做Golden Master测试用得最多的是第一种方法,简单高效,只要把所有已知的可变模式都覆盖到就行。如果断言失败了,还可以把标准化后的$normalizedExpected和$normalizedActual打印出来,一眼就能找到未预期的变化。

内容来源于stack exchange

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.04.08 11:59:33