最直接方法是用 os.pipe 临时替换 os.stdin/os.stdout:先保存原句柄,调用前替换,测试后立即恢复;写端需显式 close() 触发 EOF,避免 scanner 阻塞;ansi 序列照常输出,断言时注意处理不可见字符。

用 os.Pipe 拦截 os.Stdin 和 os.Stdout 最直接
go 程序默认从终端读写,测试时没法手动敲输入或捕获输出。最干净的办法是把 os.Stdin 和 os.Stdout 临时替换成管道(pipe),让测试代码控制流向。
关键点在于:必须在调用被测函数前替换,且测试结束后恢复原值,否则会影响其他测试或 panic(比如多次关闭同一 os.Stdin)。
- 替换前保存原始句柄:
oldStdin := os.Stdin、oldStdout := os.Stdout - 用
os.Pipe()创建一对连接,写端写入模拟输入,读端读取程序输出 - 注意
os.Stdin是*os.File,赋值时类型要一致;别误用strings.NewReader直接塞给它——会编译失败 - 测试完务必
os.Stdin = oldStdin、os.Stdout = oldStdout,尤其在并发测试中漏恢复会导致不可预测行为
fmt.Scan / bufio.Scanner 在管道里卡住?记得关写端
用 os.Pipe() 模拟输入时,如果被测代码用 fmt.Scan 或 bufio.Scanner 读取,常出现阻塞不返回——根本原因是管道的写端没关闭,读端一直等 EOF。
这不是 bug,是管道语义:只有写端关闭,读端才会收到 io.EOF,Scanner.Scan() 才返回 false。
立即学习“go语言免费学习笔记(深入)”;
- 写完模拟输入后,立刻调用
stdinPipeWriter.Close() - 不要依赖
defer关闭写端——它会在函数退出时才触发,而读操作可能早已卡住 - 若需多行输入,用
n分隔,但最后一行也得换行,否则Scanner可能不触发最后一次Scan() -
fmt.Scanf类函数对换行更敏感,建议统一用bufio.Scanner+ 显式Close()
测试含 ANSI 转义序列的输出(如颜色、清屏)怎么办
很多 CLI 工具会输出 ANSI 控制码(比如