ThinkChat🤖让你学习和工作更高效,注册即送10W Token,即刻开启你的AI之旅 广告
[TOC] ## <span style="font-size:15px">**一、testify库说明**</span> &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;Testify是一个用于编写测试的扩展包,它提供了一系列的断言函数和辅助函数,可以帮助我们编写更加简洁、易读、易维护的测试代码。它构建在Golang的原生测试框架之上,提供了更高层次的抽象和易用性 &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;Github地址:`https://github.com/stretchr/testify` &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;官方文档:`https://pkg.go.dev/github.com/stretchr/testify` ## <span style="font-size:15px">**二、安装**</span> ``` go get github.com/stretchr/testify ``` ## <span style="font-size:15px">**三、assert 包:断言工具包,辅助做测试结果判定**</span> 常用的断言函数: * assert.Equal(t, expected, actual):验证两个值是否相等。 * assert.NotEqual(t, expected, actual):验证两个值是否不相等。 * assert.True(t, condition):验证条件是否为真。 * assert.False(t, condition):验证条件是否为假。 * assert.Nil(t, value):验证值是否为nil。 * assert.NotNil(t, value):验证值是否不为nil。 * assert.Equalf(t TestingT, expected interface{}, actual interface{}, format string, args ...interface{}):用于比较两个值是否相等,并在比较失败时提供自定义的错误信息 * assert.EqualValues(t TestingT, expected, actual interface{}, msgAndArgs ...interface{}):用于比较两个值是否相等 ``` package main 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.Equal(123, 123, "they should be equal") assert.NotEqual(123, 456, "they should not be equal") assert.Nil(nil) if assert.NotNil(object) { assert.Equal("Something", object.Value) } } ``` ## <span style="font-size:15px">**四、require 包:断言工具包,辅助做测试结果判定**</span> &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;require包提供了与assert包相同的全局函数,但它们并没有返回布尔结果,而是终止了当前测试。 ``` package main import ( "github.com/stretchr/testify/require" "testing" ) type Object struct { Value string } func TestSomething(t *testing.T) { assertions := require.New(t) assertions.Equal(123, 123, "they should be equal") assertions.NotEqual(123, 456, "they should not be equal") assertions.Nil(nil) } ``` ## <span style="font-size:15px">**五、mock 包**</span> &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;使用Testify的模拟框架,我们可以创建一个模拟对象,并为其指定预期的行为,用这个模拟对象来替代真实的对象。 ### <span style="font-size:15px">**1、mock.Mock结构体函数**</span> ``` type Mock struct { ExpectedCalls []*Call // 表示预期的调用 Calls []Call // 表示实际发生的调用 } ``` | 结构体函数 | 说明 | | --- | --- | | func (m *Mock) **AssertCalled**(t TestingT, methodName string, arguments ...interface{}) bool | 检查指定的方法是否被调用,并且可以验证调用时传入的参数是否符合预期 | | func (m *Mock) **AssertNotCalled**(t TestingT, methodName string, arguments ...interface{}) bool | 断言未调用该方法 | | func (m *Mock) **AssertExpectations**(t TestingT) bool | 断言模拟对象的期望行为是否被满足,并返回断言结果的布尔值。 | | func (m *Mock) **AssertNumberOfCalls**(t TestingT, methodName string, expectedCalls int) bool | 断言该方法被调用了expectedCalls次| | func (m *Mock) **Called**(arguments ...interface{}) Arguments | 检查模拟函数是否被调用,并返回传递给模拟函数的参数列表 | | func (m *Mock) **IsMethodCallable**(t TestingT, methodName string, arguments ...interface{}) bool | IsMethodCallable检查是否可以调用该方法。如果调用方法的次数超过“Repeatability”,则返回false | | func (m *Mock) **MethodCalled**(methodName string, arguments ...interface{}) Arguments | 告诉mock对象已经调用了方法,并获取要返回的参数数组 | | func (m *Mock) **On**(methodName string, arguments ...interface{}) *Call | 设置mock的方法和参数 | ### <span style="font-size:15px">**2、mock.Call结构体函数**</span> ``` type Call struct { Parent *Mock // 指向 Mock 结构体的指针,表示该调用所属的模拟对象。 Method string // 被调用或将要被调用的方法的名称。 Arguments Arguments // 方法的参数 ReturnArguments Arguments // 当调用该方法时应该返回的参数 Repeatability int // 设置期望时返回参数的次数。0 表示总是返回该值 WaitFor <-chan time.Time // 用于阻塞 Return 直到接收到消息或被关闭的通道。如果为 nil,则立即返回。 RunFn func(Arguments) // 用于处理传递的引用参数内容的处理程序。在模拟解码器等方法时很有用。 PanicMsg *string // 用于模拟函数调用时的 panic 的消息。如果设置为非空字符串,则函数调用将会 panic,而不考虑其他设置。 } ``` <table> <thead> <th width="43%">结构体函数</th> <th width="32%">说明</th> <th width="25%">示例</th> </thead> <tbody> <tr> <td>func (c *Call) <strong>After</strong>(d time.Duration) *Call</td> <td>设置阻塞到调用返回的时间</td> <td> ``` Mock.On("MyMethod", arg1, arg2).After(time.Second) ``` </td> </tr> <tr> <td>func (c *Call) <strong>Maybe</strong>() *Call</td> <td>允许方法调用是可选的。在断言期望值时,不调用可选方法不会导致错误</td> <td></td> </tr> <tr> <td>unc (c *Call) <strong>NotBefore</strong> (calls ...*Call) *Call</td> <td>指定某个方法在另一个方法之前被调用</td> <td> ``` Mock.On("Do").Return(nil).Notbefore(Mock.On("Init").Return(nil)) ``` </td> </tr> <tr> <td>func (c *Call) <strong>On</strong>(methodName string, arguments ...interface{}) *Call</td> <td>该方法的作用是设置对特定方法的调用期望,以便在后续的测试中验证该方法是否按照期望被调用。</td> <td> ``` Mock.On("MyMethod", 1).Return(nil).On("MyOtherMethod", 'a', 'b', 'c').Return(errors.New("Some Error")) ``` </td> </tr> <tr> <td>func (c *Call) <strong>Once</strong>() *Call</td> <td>表示mock只应返回一次值</td> <td> ``` Mock.On("MyMethod", arg1, arg2).Return(returnArg1, returnArg2).Once() ``` </td> </tr> <tr> <td>func (c *Call) <strong>Panic</strong>(msg string) *Call</td> <td>当出现panic时,提示msg</td> <td> ``` Mock.On("DoSomething").Panic("test panic") ``` </td> </tr> <tr> <td>func (c *Call) <strong>Return</strong>(returnArguments ...interface{}) *Call</td> <td>指定期望的返回值</td> <td> ``` Mock.On("DoSomething").Return(errors.New("failed")) ``` </td> </tr> <tr> <td>func (c *Call) <strong>Run</strong>(fn func(args Arguments)) *Call</td> <td>Run设置在返回之前要调用的处理程序</td> <td> ``` Mock.On("Unmarshal", AnythingOfType("*map[string]interface{}")).Return().Run(func(args Arguments) { arg := args.Get(0).(*map[string]interface{}) arg["foo"] = "bar" }) ``` </td> </tr> <tr> <td>func (c *Call) <strong>Times</strong>(i int) *Call</td> <td>表示mock应该只返回指示的次数</td> <td> ``` Mock.On("MyMethod", arg1, arg2).Return(returnArg1, returnArg2).Times(5) ``` </td> </tr> <tr> <td>func (c *Call) <strong>Twice</strong>() *Call</td> <td>表示mock只应返回两次值</td> <td> ``` Mock.On("MyMethod", arg1, arg2).Return(returnArg1, returnArg2).Twice() ``` </td> </tr> <tr> <td>func (c *Call) <strong>Unset</strong>() *Call</td> <td>清除mock</td> <td> ``` test.On("func", mock.Anything).Unset() ``` </td> </tr> <tr> <td>func (c *Call) <strong>WaitUntil</strong>(w <-chan time.Time) *Call</td> <td>阻塞等待,用于测试异步方法或需要等待一定时间后才能进行断言</td> <td> ``` Mock.On("MyMethod", arg1, arg2).WaitUntil(time.After(time.Second)) ``` </td> </tr> </tbody> </table> ### <span style="font-size:15px">**3、示例**</span> &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;假设有一个名为 calculator.go 的文件,其中包含一个简单的计算器接口和实现: ``` // calculator.go package util type Calculator interface { Add(a, b int) int Subtract(a, b int) int } type MyCalculator struct{} func (c *MyCalculator) Add(a, b int) int { return a + b } func (c *MyCalculator) Subtract(a, b int) int { return a - b } ``` &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;创建一个名为 calculator_test.go 的测试文件。在这个示例中,我们创建了一个 MockCalculator 结构体,它嵌入了 `github.com/stretchr/testify/mock` 提供的 Mock 结构体。 ``` // calculator_test.go package main import ( "github.com/stretchr/testify/mock" "testing" ) type MockCalculator struct { mock.Mock } func (m *MockCalculator) Add(a, b int) int { // 重写了 Calculator 接口的方法,并使用 m.Called 和 m.On 来设置模拟对象的行为和期望 args := m.Called(a, b) return args.Int(0) } func (m *MockCalculator) Subtract(a, b int) int { args := m.Called(a, b) return args.Int(0) } func TestCalculator_Add(t *testing.T) { mockCalculator := new(MockCalculator) mockCalculator.On("Add", 3, 4).Return(6) // 调用 result := mockCalculator.Add(3, 4) if result != 6 { t.Errorf("Expected result to be 7, but got %d", result) } mockCalculator.AssertExpectations(t) } ``` <p style="display:none"> ## <span style="font-size:15px">**六、suite 包**</span> https://blog.csdn.net/qq_30614345/article/details/131314038 </p>