在测试辅助函数中处理错误

注意:本节讨论的测试辅助函数是 Go 使用的术语:这些函数用于准备测试环境和清理测试现场,而不是普通的断言设施。更多的讨论请参见 test functions 部分。

由测试辅助函数执行的操作有时会失败。例如,设置一个带有文件的目录涉及到 I/O,这可能会失败。当测试辅助函数失败时,它们的失败往往标志着测试不能继续,因为一个设置的前提条件失败了。当这种情况发生时,最好在辅助函数中调用一个Fatal函数:

// Good:
func mustAddGameAssets(t *testing.T, dir string) {
    t.Helper()
    if err := os.WriteFile(path.Join(dir, "pak0.pak"), pak0, 0644); err != nil {
        t.Fatalf("Setup failed: could not write pak0 asset: %v", err)
    }
    if err := os.WriteFile(path.Join(dir, "pak1.pak"), pak1, 0644); err != nil {
        t.Fatalf("Setup failed: could not write pak1 asset: %v", err)
    }
}

这就使调用测试辅助函数返回错误给测试本身更清晰:

// Bad:
func addGameAssets(t *testing.T, dir string) error {
    t.Helper()
    if err := os.WriteFile(path.Join(d, "pak0.pak"), pak0, 0644); err != nil {
        return err
    }
    if err := os.WriteFile(path.Join(d, "pak1.pak"), pak1, 0644); err != nil {
        return err
    }
    return nil
}

警告:调用和 t.Fatal类似函数并不总是安全的。点击这里查看更多细节

失败信息应该包括对错误的详细描述信息。这一点很重要,因为你可能会向许多用户提供测试 API,特别是在测试辅助函数中产生错误的场景增多时。用户应该知道在哪里,以及为什么产生错误。

提示: Go 1.14引入了一个t.Cleanup函数,可以用来注册清理函数,在你的测试完成后运行。该函数也适用于测试辅助函数。参见 GoTip #4: Cleaning Up Your Tests 以获得简化测试辅助程序的指导。

下面是一个名为paint_test.go的虚构文件中的片段,演示了(*testing.T).Helper如何影响 Go 测试中的失败报告:

package paint_test

import (
    "fmt"
    "testing"
)

func paint(color string) error {
    return fmt.Errorf("no %q paint today", color)
}

func badSetup(t *testing.T) {
    // This should call t.Helper, but doesn't.
    if err := paint("taupe"); err != nil {
        t.Fatalf("could not paint the house under test: %v", err) // line 15
    }
}

func mustGoodSetup(t *testing.T) {
    t.Helper()
    if err := paint("lilac"); err != nil {
        t.Fatalf("could not paint the house under test: %v", err)
    }
}

func TestBad(t *testing.T) {
    badSetup(t)
    // ...
}

func TestGood(t *testing.T) {
    mustGoodSetup(t) // line 32
    // ...
}

下面是运行该输出的一个例子。请注意突出显示的文本和它的不同之处:

=== RUN   TestBad
    paint_test.go:15: could not paint the house under test: no "taupe" paint today
--- FAIL: TestBad (0.00s)
=== RUN   TestGood
    paint_test.go:32: could not paint the house under test: no "lilac" paint today
--- FAIL: TestGood (0.00s)
FAIL

paint_test.go:15的错误是指在badSetup中失败的设置函数的那一行: t.Fatalf("could not paint the house under test: %v", err)paint_test.go:32指的是在TestGood中失败的那一行测试: goodSetup(t)

正确地使用(*testing.T).Helper可以更好地归纳出失败的位置,当:

  • 辅助函数数量增加
  • 在辅助函数中使用其他的辅助函数
  • 测试函数使用辅助函数的数量增加

提示:如果一个辅助函数调用(*testing.T).Error(*testing.T).Fatal,在格式字符串中提供一些上下文,以帮助确定出错的原因。

提示:如果一个辅助函数没有做任何事情会导致测试失败,那么它就不需要调用t.Helper。通过从函数参数列表中删除t来简化其签名。