包装 testing.T.Errorf() 时显示原始源代码行

Show original source line when wrapping testing.T.Errorf()

我正在为 Go 模块编写一些测试。其中很多是检查函数 return 的正确值。这是我目前正在做的一个简单示例:

package foo

import (
    "reflect"
    "testing"
)

func Foo() int {
    return 3
}

func TestFoo(t *testing.T) {
    expected := 4
    actual := Foo()

    if !reflect.DeepEqual(actual, expected) {
        t.Errorf("actual=%v, expected=%v", actual, expected)
    }
}

一个测试可能包含许多这样的相等性检查。为每个检查重复这 6 行使得测试难以阅读并且容易出错(根据我过去几天的经验)。所以我想我会做一个 assertEquals() 函数来包装整个逻辑,类似于其他测试框架提供的(例如 JUnit's org.junit.Assert):

func TestFoo(t *testing.T) {
    assertEqual(t, 4, Foo())
}

func assertEqual(t *testing.T, actual interface{}, expected interface{}) {
    if !reflect.DeepEqual(actual, expected) {
        t.Errorf("Assertion failed: %v != %v", actual, expected)
    }
}

现在的问题是Errorf()显然不会显示调用assertEqual()的行,而是显示assertEqual内部对Errorf()的调用:

=== RUN   TestFoo
    foo_test.go:28: Assertion failed: 4 != 3
--- FAIL: TestFoo (0.00s)

有没有办法解决这个问题,例如通过显示整个堆栈跟踪而不是仅显示对 Errorf()?

的调用位置

或者是否有更好的方法来避免重复这些代码行来检查函数的 return 值?

您可以使用 t.Helper():

Helper marks the calling function as a test helper function. When printing file and line information, that function will be skipped. Helper may be called simultaneously from multiple goroutines.

因此您的辅助函数变为:

func assertEqual(t *testing.T, actual interface{}, expected interface{}) {
    t.Helper()
    if !reflect.DeepEqual(actual, expected) {
        t.Errorf("Assertion failed: %v != %v", actual, expected)
    }
}

输出:

=== RUN   TestFoo
    prog.go:13: Assertion failed: 4 != 3
--- FAIL: TestFoo (0.00s)
FAIL

其中 this playground 中的 prog.go:13 是主要测试目标 中调用 assertEqual 而不是 t.Errorf 的行它。

这只会更改测试输出中的文件行。如果你真的想要完整的堆栈跟踪,你可以使用 runtime.Caller 中提到的 threads.