为了账号安全,请及时绑定邮箱和手机立即绑定

测试包含 stderr、stdout 和 os.Exit() 的函数

测试包含 stderr、stdout 和 os.Exit() 的函数

Go
翻翻过去那场雪 2022-06-13 15:52:04
我正在为 cli 应用程序构建 UI。我完成了这些功能,但我不知道如何测试它。回购:https ://github.com/erdaltsksn/cuifunc Success(message string) {    color.Success.Println("√", message)    os.Exit(0)}// Error prints a success message and exit status 1func Error(message string, err ...error) {    color.Danger.Println("X", message)    if len(err) > 0 {        for _, e := range err {            fmt.Println(" ", e.Error())        }    }    os.Exit(1)}我想为函数编写单元测试。问题是函数包含print和os.Exit()。我不知道如何为两者编写测试。本主题:如何在单元测试中测试函数的输出 (stdout/stderr)有助于我测试打印函数。我需要添加os.Exit()我现在的解决方案:func captureOutput(f func()) string {    var buf bytes.Buffer    log.SetOutput(&buf)    f()    log.SetOutput(os.Stderr)    return buf.String()}func TestSuccess(t *testing.T) {    type args struct {        message string    }    tests := []struct {        name   string        args   args        output string    }{        {"Add test cases.", args{message: "my message"}, "ESC[1;32m"},    }    for _, tt := range tests {        t.Run(tt.name, func(t *testing.T) {            want := tt.output            got := captureOutput(func() {                cui.Success(tt.args.message)            })            got := err            if got.Error() != want {                t.Error("Got:", got, ",", "Want:", want)            }        })    }}
查看完整描述

2 回答

?
富国沪深

TA贡献1790条经验 获得超9个赞

TDD 中通常的答案是您将函数分为两部分;一个易于测试的部分,但与特定文件句柄或 os::Exit 的特定实现不紧密耦合;另一部分与这些东西是紧密耦合的,但是如此简单,显然没有任何不足之处。


您的“单元测试”是测量第一部分的错误检测器。


第二部分你写一次,“用手”检查它,然后不管它。这里的想法是事情是如此简单,一旦正确实施,它们就不需要改变。


// Warning: untested code ahead

func Foo_is_very_stable() {

    bar_is_easy_to_test(stdin, stdout, os.exit)

}


func bar_is_easy_to_test(in *File, out *File , exit func(int)) {

    // Do complicated things here.

现在,我们有点作弊——os.exit这是一种永远不会回来的特殊魔法,但bar_is_easy_to_test并不真正知道这一点。


另一种更公平一点的设计是将复杂的代码放入状态机中。状态机决定做什么,调用机器的主机决定如何做……


// More untested code

switch state_machine.next() {

    case OUT:

        println(state_machine.line())

        state_machine.onOut()

    case EXIT:

        os.exit(state_machine.exitCode())

同样,您会得到一个易于测试的复杂部分(状态机)和一个更简单的稳定且易于通过检查验证的部分。


这是 TDD 的核心思想之一——我们故意以“易于测试”的方式设计我们的代码。这样做的理由是声称易于测试的代码也易于维护(因为很容易检测到错误并且因为设计本身“更干净”)。


查看完整回答
反对 回复 2022-06-13
?
Qyouu

TA贡献1786条经验 获得超11个赞

你所拥有的东西被称为“副作用” - 当你的应用程序的执行跨越它的环境时,它是地址空间的情况。问题是,你不测试副作用。这并不总是可能的,而且当它是 - 它是不合理的复杂和丑陋的。

基本思想是让你的副作用,如 CLI 输出或os.Exit()(或网络连接,或访问文件),与你的逻辑主体分离。有很多方法可以做到这一点,整个“软件设计”学科都致力于此,@VoiceOfUnreason 给出了几个可行的例子。

在您的示例中,我将在函数中包装副作用并安排某种方式将依赖项注入Success()Error()。如果您想保留这两个简单的函数,那么它要么是函数参数,要么是持有退出函数的全局变量(根据@Peter 的评论),但我建议采用 OO 方式,采用一些 模式并实现更大为您提供灵活性。


查看完整回答
反对 回复 2022-06-13
  • 2 回答
  • 0 关注
  • 229 浏览
慕课专栏
更多

添加回答

举报

0/150
提交
取消
微信客服

购课补贴
联系客服咨询优惠详情

帮助反馈 APP下载

慕课网APP
您的移动学习伙伴

公众号

扫描二维码
关注慕课网微信公众号