在其他语言中处理测试时,你可能熟悉使用断言和期望。但是,在Go中,我找不到类似的东西,直到我发现了
testify包,Testify的 assert 包为我们的测试提供了各种有用的工具。
Github地址:https://github.com/stretchr/testify
官方文档:https://pkg.go.dev/github.com/stretchr/testify
go get github.com/stretchr/testify
package go_testify
import (
"github.com/stretchr/testify/assert"
"testing"
)
type Object struct {
Value string
}
func TestSomething(t *testing.T) {
var object = Object{Value: "Something"}
// assert equality
assert.Equal(t, 123, 123, "they should be equal")
// assert inequality
assert.NotEqual(t, 123, 456, "they should not be equal")
// assert for nil (good for errors)
assert.Nil(t, nil)
// assert for not nil (good when you expect something)
if assert.NotNil(t, object) {
// now we know that object isn't nil, we are safe to make
// further assertions without causing any errors
assert.Equal(t, "Something", object.Value)
}
}
$ go test -v 001_test.go
=== RUN TestSomething
--- PASS: TestSomething (0.00s)
PASS
ok command-line-arguments 0.290s
如果多次断言,请使用以下命令:
package go_testify
import (
"github.com/stretchr/testify/assert"
"testing"
)
type Object struct {
Value string
}
func TestSomething(t *testing.T) {
var object = Object{Value: "Something"}
assert := assert.New(t)
// assert equality
assert.Equal(123, 123, "they should be equal")
// assert inequality
assert.NotEqual(123, 456, "they should not be equal")
// assert for nil (good for errors)
assert.Nil(nil)
// assert for not nil (good when you expect something)
if assert.NotNil(object) {
// now we know that object isn't nil, we are safe to make
// further assertions without causing any errors
assert.Equal("Something", object.Value)
}
}
$ go test -v 002_test.go
=== RUN TestSomething
--- PASS: TestSomething (0.00s)
PASS
ok command-line-arguments 0.033s
require包提供了与assert包相同的全局函数,但它们并没有返回布尔结果,而是终止了当前测试。
package go_testify
import (
"github.com/stretchr/testify/require"
"testing"
)
type Object struct {
Value string
}
func TestSomething(t *testing.T) {
var object = Object{Value: "Something"}
require.Equal(t, 123, 123, "they should be equal")
require.NotEqual(t, 123, 456, "they should not be equal")
require.Nil(t, nil)
require.NotNil(t, object)
}
$ go test -v 003_test.go
=== RUN TestSomething
--- PASS: TestSomething (0.00s)
PASS
ok command-line-arguments 0.261s
package go_testify
import (
"github.com/stretchr/testify/require"
"testing"
)
type Object struct {
Value string
}
func TestSomething(t *testing.T) {
var object = Object{Value: "Something"}
require := require.New(t)
require.Equal(123, 123, "they should be equal")
require.NotEqual(123, 456, "they should not be equal")
require.Nil(nil)
require.NotNil(t, object)
}
$ go test -v 004_test.go
=== RUN TestSomething
--- PASS: TestSomething (0.00s)
PASS
ok command-line-arguments 0.250s
mock包提供了一种用于轻松编写mock对象的机制,在编写测试代码时可以使用该机制来代替真实对象。
package go_testify
import (
"fmt"
"github.com/stretchr/testify/mock"
"testing"
)
// Test objects
// MyMockedObject是一个模拟对象,它实现了一个接口,该接口描述了我正在测试的代码所依赖的对象
type MyMockedObject struct {
mock.Mock
}
// DoSomething是MyMockedObject上的一个方法,它实现了一些接口,只记录活动,并返回Mock对象告诉的内容
// 在真实的对象中,这个方法会做一些有用的事情,但由于这是一个模拟对象,我们只需要将其截断
func (m *MyMockedObject) DoSomething(number int) (bool, error) {
args := m.Called(number)
return args.Bool(0), args.Error(1)
}
func targetFuncThatDoesSomethingWithObj1(myMockedObject *MyMockedObject) (bool, error) {
b, err := myMockedObject.DoSomething(123)
if err != nil {
return false, fmt.Errorf("failed, error details: %w", err)
}
return b, nil
}
// 实际测试功能
// TestSomething是一个如何使用我们的测试对象来断言我们正在测试的一些目标代码的示例
func TestSomething(t *testing.T) {
testObj := new(MyMockedObject)
testObj.On("DoSomething", 123).Return(true, nil)
targetFuncThatDoesSomethingWithObj1(testObj)
testObj.AssertExpectations(t)
}
// TestSomethingWithPlaceholder是关于如何使用我们的测试对象来断言我们正在测试的一些目标代码的第二个例子
// 这次使用占位符,当传入的数据通常是动态生成的,并且无法预先预测时可能会使用占位符
func TestSomethingWithPlaceholder(t *testing.T) {
testObj := new(MyMockedObject)
testObj.On("DoSomething", mock.Anything).Return(true, nil)
targetFuncThatDoesSomethingWithObj2(testObj)
testObj.AssertExpectations(t)
}
func targetFuncThatDoesSomethingWithObj2(myMockedObject *MyMockedObject) (bool, error) {
b, err := myMockedObject.DoSomething(123)
if err != nil {
return false, fmt.Errorf("failed, error details: %w", err)
}
return b, nil
}
// 展示了如何使用Unset方法清理处理程序,然后添加新的处理程序
func TestSomethingElse2(t *testing.T) {
testObj := new(MyMockedObject)
mockCall := testObj.On("DoSomething", mock.Anything).Return(true, nil)
targetFuncThatDoesSomethingWithObj3(testObj)
testObj.AssertExpectations(t)
mockCall.Unset()
testObj.On("DoSomething", mock.Anything).Return(false, nil)
testObj.AssertExpectations(t)
}
func targetFuncThatDoesSomethingWithObj3(myMockedObject *MyMockedObject) (bool, error) {
b, err := myMockedObject.DoSomething(123)
if err != nil {
return false, fmt.Errorf("failed, error details: %w", err)
}
return b, nil
}
$ go test -v 005_test.go
=== RUN TestSomething
005_test.go:36: PASS: DoSomething(int)
--- PASS: TestSomething (0.00s)
=== RUN TestSomethingWithPlaceholder
005_test.go:45: PASS: DoSomething(string)
--- PASS: TestSomethingWithPlaceholder (0.00s)
=== RUN TestSomethingElse2
005_test.go:61: PASS: DoSomething(string)
005_test.go:64: PASS: DoSomething(string)
--- PASS: TestSomethingElse2 (0.00s)
PASS
ok command-line-arguments 0.262s
suite 包您可以使用结构体构建测试套件,在结构体上定义设置/拆卸和测试方法,并像平常一样使用 go test 运行
它们。
package go_testify
import (
"testing"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/suite"
)
type ExampleTestSuite struct {
suite.Suite
VariableThatShouldStartAtFive int
}
// before each test
func (suite *ExampleTestSuite) SetupTest() {
suite.VariableThatShouldStartAtFive = 5
}
func (suite *ExampleTestSuite) TestExample() {
assert.Equal(suite.T(), 5, suite.VariableThatShouldStartAtFive)
}
func TestExampleTestSuite(t *testing.T) {
suite.Run(t, new(ExampleTestSuite))
}
$ go test -v 006_test.go
=== RUN TestExampleTestSuite
=== RUN TestExampleTestSuite/TestExample
--- PASS: TestExampleTestSuite (0.00s)
--- PASS: TestExampleTestSuite/TestExample (0.00s)
PASS
ok command-line-arguments 0.371s
要获得一个更完整的示例,使用 suite 包提供的所有功能,请查看我们的示例测试suite :
package go_testify
import (
"testing"
"github.com/stretchr/testify/suite"
)
type ExampleTestSuite struct {
suite.Suite
VariableThatShouldStartAtFive int
}
// before each test
func (suite *ExampleTestSuite) SetupTest() {
suite.VariableThatShouldStartAtFive = 5
}
func (suite *ExampleTestSuite) TestExample() {
suite.Equal(suite.VariableThatShouldStartAtFive, 5)
}
func TestExampleTestSuite(t *testing.T) {
suite.Run(t, new(ExampleTestSuite))
}
$ go test -v 007_test.go
=== RUN TestExampleTestSuite
=== RUN TestExampleTestSuite/TestExample
--- PASS: TestExampleTestSuite (0.00s)
--- PASS: TestExampleTestSuite/TestExample (0.00s)
PASS
ok command-line-arguments 0.036s
func Equal(t TestingT, expected, actual interface{}, msgAndArgs ...interface{}) bool
func NotEqual(t TestingT, expected, actual interface{}, msgAndArgs ...interface{}) bool
func Nil(t TestingT, object interface{}, msgAndArgs ...interface{}) bool
func NotNil(t TestingT, object interface{}, msgAndArgs ...interface{}) bool
func Empty(t TestingT, object interface{}, msgAndArgs ...interface{}) bool
func NotEmpty(t TestingT, object interface{}, msgAndArgs ...interface{}) bool
func NoError(t TestingT, err error, msgAndArgs ...interface{}) bool
func Error(t TestingT, err error, msgAndArgs ...interface{}) bool
func Zero(t TestingT, i interface{}, msgAndArgs ...interface{}) bool
func NotZero(t TestingT, i interface{}, msgAndArgs ...interface{}) bool
func True(t TestingT, value bool, msgAndArgs ...interface{}) bool
func False(t TestingT, value bool, msgAndArgs ...interface{}) bool
func Len(t TestingT, object interface{}, length int, msgAndArgs ...interface{}) bool
func NotContains(t TestingT, s, contains interface{}, msgAndArgs ...interface{}) bool
func NotContains(t TestingT, s, contains interface{}, msgAndArgs ...interface{}) bool
func Subset(t TestingT, list, subset interface{}, msgAndArgs ...interface{}) (ok bool)
func NotSubset(t TestingT, list, subset interface{}, msgAndArgs ...interface{}) (ok bool)
func FileExists(t TestingT, path string, msgAndArgs ...interface{}) bool
func DirExists(t TestingT, path string, msgAndArgs ...interface{}) bool