🗂
【Golang】stretchr/testifyパッケージを使ってみる
はじめに
この記事では、Goのテスト用のパッケージのstretchr/testify
の使い方を書き留めています。
stretchr/testifyのインストール
Go Modulesを使っている場合は、次のコマンドでstretchr/testifyをインストールできます。
$ go get github.com/stretchr/testify
サンプルコード
テストの対象とするサンプルコードを以下に示します。
package foo
import "fmt"
type Person struct {
name Name
}
func NewPerson(name Name) *Person {
return &Person{name: name}
}
func (p *Person) Name() string {
return p.name.Value()
}
func (p *Person) PrintName() {
fmt.Println(p.name.Value())
}
type Name interface {
Value() string
}
type NameImpl struct {
value string
}
func NewNameImpl(value string) *NameImpl {
return &NameImpl{value: value}
}
func (n *NameImpl) Value() string {
return n.value
}
testify/assertパッケージの利用例
assertパッケージには、Equal
やNotEqual
、Nil
等の関数が用意されています。
package foo
import (
"testing"
"github.com/stretchr/testify/assert"
)
func TestPerson_Name(t *testing.T) {
p := NewPerson(NewNameImpl("foo"))
assert.Equal(t, "foo", p.Name())
}
testify/mockパッケージの利用例
mockパッケージを利用すると、モックの構造体を用意して、テストで使用することができます。
モックの構造体には、モック対象のインタフェースをimplementした構造体を作成し、mock.Mock
を埋め込みます。
package foo
import (
"testing"
"github.com/stretchr/testify/mock"
)
type MockedName struct {
mock.Mock
}
var _ Name = (*MockedName)(nil)
func (m *MockedName) Value() string {
args := m.Called()
return args.String(0)
}
func TestPerson_PrintName(t *testing.T) {
testObj := new(MockedName)
testObj.On("Value").Return("Something").Once() // (1)
p := NewPerson(testObj)
p.PrintName()
testObj.AssertExpectations(t)
}
(1)では、モック対象のメソッドや戻り値、回数を指定しています。
vektra/mockeryを使ってモックを自動生成する
上記のコードでは、MockedName
を作成していましたが、モックはvektra/mockery
を使用して生成することができます。
Dockerイメージをpull
vektra/mockery
はDockerを使って実行できるので、イメージをpullします。
$ docker pull vektra/mockery
モックコードを自動生成する
次のコマンドを実行すると、プロジェクト配下のコードのモックが生成されます。
$ docker run -v "$PWD":/src -w /src vektra/mockery --all
今回使用したサンプルコードを対象にコマンドを実行した結果、mocks/Name.go
が生成されました。
// Code generated by mockery v2.18.0. DO NOT EDIT.
package mocks
import mock "github.com/stretchr/testify/mock"
// Name is an autogenerated mock type for the Name type
type Name struct {
mock.Mock
}
// Value provides a mock function with given fields:
func (_m *Name) Value() string {
ret := _m.Called()
var r0 string
if rf, ok := ret.Get(0).(func() string); ok {
r0 = rf()
} else {
r0 = ret.Get(0).(string)
}
return r0
}
type mockConstructorTestingTNewName interface {
mock.TestingT
Cleanup(func())
}
// NewName creates a new instance of Name. It also registers a testing interface on the mock and a cleanup function to assert the mocks expectations.
func NewName(t mockConstructorTestingTNewName) *Name {
mock := &Name{}
mock.Mock.Test(t)
t.Cleanup(func() { mock.AssertExpectations(t) })
return mock
}
生成されたコードを使用するには、テストコードで生成する構造体を変更します。
func TestPerson_PrintName(t *testing.T) {
testObj := new(mocks.Name) // 自動生成したモックに書き換え
// 略
}
Discussion