英文:
Show original source line when wrapping testing.T.Errorf()
问题
我正在为一个Go模块编写一些测试。其中很大一部分是检查函数是否返回正确的值。以下是我目前正在做的一个简单示例:
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的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()
的位置?
或者有没有更好的方法来避免重复这些代码行来检查函数的返回值?
英文:
I'm writing some test for a Go module. A lot of it is checking that functions return the correct values. This is a simple example for what I'm currently doing:
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)
}
}
A single test might contain a many such equality checks. Duplicating these 6 lines for each check makes the tests hard to read and error-prone to write (from what I've experienced the past few days). So I thought I'd make an assertEquals()
function that wraps the whole logic similar to what other testing frameworks provide (e.g. in 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)
}
}
The problem now is that Errorf()
will obviously not show the line where assertEqual()
is called but the call to Errorf()
inside of assertEqual
:
=== RUN TestFoo
foo_test.go:28: Assertion failed: 4 != 3
--- FAIL: TestFoo (0.00s)
Is there a way to fix that, e.g. by showing the whole stack trace instead of only the location of the call to Errorf()
?
Or is there a better way to avoid repeating those lines of code to check the return value of a function?
答案1
得分: 3
你可以使用t.Helper()
:
Helper将调用的函数标记为测试辅助函数。在打印文件和行信息时,该函数将被跳过。Helper可以同时从多个goroutine中调用。
所以你的辅助函数变成了:
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
其中,prog.go:13
在this playground中是主测试目标中调用assertEqual
而不是在其中调用t.Errorf
的行。
这只会改变测试输出中的文件行。如果你实际上想要完整的堆栈跟踪,你可以使用runtime.Caller
,如这些 两个线程中所提到的。
英文:
You can use 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.
So your helper function becomes:
func assertEqual(t *testing.T, actual interface{}, expected interface{}) {
t.Helper()
if !reflect.DeepEqual(actual, expected) {
t.Errorf("Assertion failed: %v != %v", actual, expected)
}
}
Output:
=== RUN TestFoo
prog.go:13: Assertion failed: 4 != 3
--- FAIL: TestFoo (0.00s)
FAIL
where prog.go:13
in this playground is the line in the main test target that calls assertEqual
instead of t.Errorf
within it.
This will only change the file line in the testing output. If you actually want the full stack trace, you can use runtime.Caller
as mentioned in these two threads.
通过集体智慧和协作来改善编程学习和解决问题的方式。致力于成为全球开发者共同参与的知识库,让每个人都能够通过互相帮助和分享经验来进步。
评论