我意识到我可以将testing.T
类型传递给需要处理测试接口的函数。但是扩展结构并使用它怎么样?我知道我可以键入扩展,但不幸的是这不起作用:
package test
import "testing"
type testingT testing.T
func (t *testingT) assert(val int) {
if val == 0 {
t.Errorf("%d is zero", val)
}
}
func TestSomething(t *testingT) {
t.assert(0)
}
一种解决方案是嵌入,正如您在 eduncan911 的答案中看到的那样。如果要将其他字段添加到新结构中,那就太好了。
但是,这不是必需的。创建一个以 testing.T
作为其基础类型的新类型(就像您所做的那样(就足够了:
type myT testing.T
func (t *myT) assert(val int) {
if val == 0 {
t.Errorf("%d is zero", val)
}
}
使用它
在TestSomething()
我们不能只称t.assert(0)
,assert()
因为它不是一种testing.T
方法,而是我们的myT
类型。所以我们需要一个类型 myT
的值,我们可以调用 myT.assert()
.
但是,我们不能使用类型断言将testing.T
转换为我们的myT
类型,因为类型断言要求表达式为接口类型!t
不是接口类型,而是具体类型:*testing.T
。
解决方案非常简单,我们可以使用简单的类型转换,因为myT
和testing.T
具有相同的底层类型:
my := myT(*t) // Conversion valid, but not yet enough, read on...
以上有效,我们可以调用:
my.assert(0)
但是在运行测试时,它不会失败!为什么?这是因为上述*t
取消引用、转换和赋值将创建一个副本,而my
将是 myT
类型,但将是 *t
的副本,因此当我们调用 myT.assert()
时,它得到一个指针,但一个指向副本的指针。当myT.assert()
调用t.Errorf()
时,它会在副本上注册错误,因此测试框架不会看到此Errorf()
调用。
"最终"解决方案很简单:使用类型转换,但不转换*t
,而是t
本身(这是一个指针(。当然*t
不能转换为myT
,只能转换为*myT
。并且此类型需要用括号括起来(以避免歧义(:
func TestSomething(t *testing.T) {
my := (*myT)(t)
my.assert(0)
}
现在我们很高兴,一切都按预期进行。
注意:如果您不想调用多个myT
方法,也可以在一行中调用myT.assert()
:
func TestSomething(t *testing.T) {
(*myT)(t).assert(0)
}
您可以使用嵌入来添加所有者方法:
package test
import "testing"
type testingT struct {
*testing.T
}
func (t *testingT) assert(val int) {
if val == 0 {
t.Errorf("%d is zero", val)
}
}
func TestSomething(t *testing.T) {
t2 := &testingT{t}
t2.assert(0)
}
准备自定义测试结构:
type T testing.T
func (t *T) AssertEqualsInt(expected, actual int) {
if actual != expected {
// Correct point for file and line where happened error
t.Helper()
t.Errorf("Values not equal expected: %d, got: %d", expected, actual)
}
}
func (t *T) AssertEqualsString(expected, actual string) {
if actual != expected {
t.Helper()
t.Errorf("Values not equal expected: %s, got: %s", expected, actual)
}
}
比在测试中使用它:
func TestFooBarBaz(t *testing.T) {
(*T)(t).AssertEqualsInt(0, 10)
(*T)(t).AssertEqualsString("foo", "bar")
}