概念定义
套件不再是独立实验的集合,而是一条意外流水线:每个测试假设前驱的残留。执行顺序的任何变化——分片、乱序、按字母序插入新测试——都让假设失效。
工作机制
耦合渠道:共享数据库状态、模块与类级可变状态、先前测试设置的环境与配置、文件系统残留。检测靠多排列运行——随机顺序插件标记配对,bisect 模式隔离最小失败序列;修复落在隔离层——每测试事务、模块重新导入、显式重置夹具。
使用场景
启用并行之前、引入重试策略之后、以及“失败聚在特定顺序而非特定测试”之时。CI 定期随机排序能防止耦合重新生长。
常见误区
“本地通过就正确”是错觉:本地通常固定同一顺序,耦合只是在等第一次重排。第一反应“改回能过的顺序”把隐形图固化为政策,重试也只会掩蔽。耦合不只来自共享数据库——单例、类重载、环境变量、时钟状态同样算数。并行分片让同一对测试只在恰好同时调度时相撞,产出最难懂的 flaky 签名。
代码示例
import tempfile
from contextlib import contextmanager
from dataclasses import dataclass
from pathlib import Path
from typing import Iterator, Optional
class SandboxEscapeError(ValueError):
"""Raised when a requested path would leave the sandbox root."""
@dataclass(frozen=True)
class Sandbox:
root: Path
def resolve(self, *parts: str) -> Path:
target = self.root.joinpath(*parts).resolve()
if target != self.root and self.root not in target.parents:
raise SandboxEscapeError("path escapes the sandbox: " + str(target))
return target
def write(self, relative_path: str, content: str) -> Path:
target = self.resolve(relative_path)
target.parent.mkdir(parents=True, exist_ok=True)
target.write_text(content, encoding="utf-8")
return target
def read(self, relative_path: str) -> str:
return self.resolve(relative_path).read_text(encoding="utf-8")
def exists(self, relative_path: str) -> bool:
return self.resolve(relative_path).exists()
@contextmanager
def sandbox(prefix: Optional[str] = None) -> Iterator[Sandbox]:
"""Yield a private temporary sandbox and always remove it afterwards."""
with tempfile.TemporaryDirectory(prefix=prefix or "devfactory-") as raw:
yield Sandbox(root=Path(raw).resolve())
package calc
import "testing"
func Divide(a, b float64) (float64, bool) {
if b == 0 {
return 0, false
}
return a / b, true
}
func TestDivide(t *testing.T) {
cases := []struct {
name string
a, b float64
want float64
wantOK bool
}{
{name: "even", a: 10, b: 2, want: 5, wantOK: true},
{name: "fraction", a: 1, b: 4, want: 0.25, wantOK: true},
{name: "divide by zero", a: 1, b: 0, want: 0, wantOK: false},
}
for _, tc := range cases {
t.Run(tc.name, func(t *testing.T) {
got, ok := Divide(tc.a, tc.b)
if ok != tc.wantOK || got != tc.want {
t.Fatalf("got (%v, %v), want (%v, %v)", got, ok, tc.want, tc.wantOK)
}
})
}
}