Testify를 사용한 golang unittest

jeonghyeon·2022년 7월 17일
1

Testify란?

Testify는 테스트를 위한 여러가지 패키지가 포함된 툴입니다. “쉬운 assertion”, ‘mocking”, “test suite” 기능을 제공합니다.

Testify 사용하기

assert

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 함수는 testing.T 객체를 첫 번째 인자로 받습니다.
  • 모든 assert 함수는 성공 여부를 나타내는 bool 값을 반환합니다. 이 반환 값을 사용하여 특정 조건 하에 수행해야 할 테스트를 수행할 수 있습니다.

assert를 많이 사용해야 할 경우 아래와 같이 사용할 수 있습니다.

func TestSomething(t *testing.T) {
	assert := assert.New(t)

	assert.Equal(123, 123, "they should be equal")
}

suite

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))
}
  • AfterTest(suiteName, testName string): 테스트 종료 후 실행
  • BeforeTest(suiteName, testName string): 테스트 시작 전 실행
  • SetupSuite(): 모든 테스트 시작 전 실행
  • SetupTest(): 각 테스트 시작 전 실행
  • TearDownSuite(): suite의 모든 테스트 종료 후 실행
  • TearDownTest(): 각 테스트 종료 후 실행
=== 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 패키지를 사용하여 테스트를 위한 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

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
}

0개의 댓글