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

huangapple go评论77阅读模式
英文:

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:13this 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.

huangapple
  • 本文由 发表于 2022年2月25日 21:11:48
  • 转载请务必保留本文链接:https://go.coder-hub.com/71266376.html
匿名

发表评论

匿名网友

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定