Testify는 테스트를 위한 여러가지 패키지가 포함된 툴입니다. “쉬운 assertion”, ‘mocking”, “test suite” 기능을 제공합니다.
package main
import (
"testing"
"github.com/stretchr/testify/assert"
)
func TestSomething(t *testing.T) {
// 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
assert.Nil(t, nil)
// assert for not nil
assert.NotNil(t, true)
}
assert를 많이 사용해야 할 경우 아래와 같이 사용할 수 있습니다.
func TestSomething(t *testing.T) {
assert := assert.New(t)
assert.Equal(123, 123, "they should be equal")
}
suite를 사용하면 setup/teardown 메소드를 만들 수 있습니다.
package main
import (
"testing"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/suite"
)
type ExampleTestSuite struct {
suite.Suite
VariableThatShouldStartAtFive int
}
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))
}
=== RUN TestExampleTestSuite
SetupSuite()
=== RUN TestExampleTestSuite/TestExample
SetupTest()
BeforeTest ExampleTestSuite TestExample
AfterTest ExampleTestSuite TestExample
TearDownTest()
=== RUN TestExampleTestSuite/TestExample2
SetupTest()
BeforeTest ExampleTestSuite TestExample2
AfterTest ExampleTestSuite TestExample2
TearDownTest()
TearDownSuite()
--- PASS: TestExampleTestSuite (0.00s)
--- PASS: TestExampleTestSuite/TestExample (0.00s)
--- PASS: TestExampleTestSuite/TestExample2 (0.00s)
PASS
ok testify-test 0.002s
mock 패키지를 사용하여 테스트를 위한 mock 객체를 생성할 수 있습니다.
type MyMockObject struct {
// embedding Mock
mock.Mock
}
func (m *MyMockObject) DoSomething(number int) (bool, error) {
// mock 객체가 주어진 인자(number)를 사용해서 호출됨을 기록
args := m.Called(number)
return args.Bool(0), args.Error(1)
}
func targetFuncThatDoesSomethingWithObj(testObj *MyMockObject) {
flag, err := testObj.DoSomething(123)
fmt.Printf("targetFuncThatDoesSomethingWithObj: %v, %v\n", flag, err)
}
func TestSomething(t *testing.T) {
testObj := new(MyMockObject)
testObj.On("DoSomething", 123).Return(true, errors.New("Mock Error"))
targetFuncThatDoesSomethingWithObj(testObj)
testObj.AssertExpectations(t)
}
=== RUN TestSomething
targetFuncThatDoesSomethingWithObj: true, Mock Error
main_test.go:76: PASS: DoSomething(int)
--- PASS: TestSomething (0.00s)
PASS
mockery는 testify/mock 을 사용해 mock 객체를 자동으로 생성해줍니다.
mockery 설치
go install github.com/vektra/mockery/v2@latest
$ mockery --all
16 Jul 22 16:35 KST INF Starting mockery dry-run=false version=v2.14.0
16 Jul 22 16:35 KST INF Walking dry-run=false version=v2.14.0
16 Jul 22 16:35 KST INF Generating mock dry-run=false interface=SomeService qualified-name=testify-test version=v2.14.0
Mock 객체를 생성할 Interface
type SomeService interface {
DoSometing(string, bool) (string, error)
}
생성된 Mock 객체
// Code generated by mockery v2.14.0. DO NOT EDIT.
package mocks
import mock "github.com/stretchr/testify/mock"
// SomeService is an autogenerated mock type for the SomeService type
type SomeService struct {
mock.Mock
}
// DoSometing provides a mock function with given fields: _a0, _a1
func (_m *SomeService) DoSometing(_a0 string, _a1 bool) (string, error) {
ret := _m.Called(_a0, _a1)
var r0 string
if rf, ok := ret.Get(0).(func(string, bool) string); ok {
r0 = rf(_a0, _a1)
} else {
r0 = ret.Get(0).(string)
}
var r1 error
if rf, ok := ret.Get(1).(func(string, bool) error); ok {
r1 = rf(_a0, _a1)
} else {
r1 = ret.Error(1)
}
return r0, r1
}
type mockConstructorTestingTNewSomeService interface {
mock.TestingT
Cleanup(func())
}
// NewSomeService creates a new instance of SomeService. It also registers a testing interface on the mock and a cleanup function to assert the mocks expectations.
func NewSomeService(t mockConstructorTestingTNewSomeService) *SomeService {
mock := &SomeService{}
mock.Mock.Test(t)
t.Cleanup(func() { mock.AssertExpectations(t) })
return mock
}