🕌

乱数生成を含む実装におけるユニットテストの書き方

2024/01/31に公開

概要

  • 表記の通り。

背景

  • 実務でやったことの記録用。
  • 最近アウトプットあんまりやれてなく、これから継続して記事を書いていくきっかけにと本記事を書いたので、内容は簡素です。

実装再現

  1. 乱数生成部分のインターフェイスと実装
const charSet = "abcdefghijkmnpqrstuvwxyzABCDEFGHJKLMNPQRSTUVWXYZ23456789"

func init() {
	rand.Seed(time.Now().UnixNano())
}

type Generator interface {
	GenerateString(size int) string
}

type Rand struct{}

func New() *Rand {
	return &Rand{}
}

func (r *Rand) GenerateString(size int) string {
	p := make([]byte, size)
	for i := range p {
		p[i] = charSet[rand.Intn(len(charSet))]
	}
	return string(p)
}
  1. mockeryでモック生成
// Code generated by mockery v2.36.1. DO NOT EDIT.

package rand

import mock "github.com/stretchr/testify/mock"

// MockGenerator is an autogenerated mock type for the Generator type
type MockGenerator struct {
	mock.Mock
}

type MockGenerator_Expecter struct {
	mock *mock.Mock
}

func (_m *MockGenerator) EXPECT() *MockGenerator_Expecter {
	return &MockGenerator_Expecter{mock: &_m.Mock}
}
~~~ 省略 ~~~
  1. 実装
  • 下記の実装例は、passwordを乱数生成してAccountの作成、パスワードを返却することを想定してます。
type AccountAccessor interface {
	Create(ctx context.Context, email string) (string, error)
}

type Account struct {
	account persistence.Account
	rand    rand.Generator
}

func NewAccount(
	account persistence.Account,
	rand rand.Generator,
) AccountAccessor {
	return &Account{
		account: account,
		rand:    rand,
	}
}

func (a *Account) Create(ctx context.Context, email string) (string, error) {
	account := &account.Model{
		Email: email,
	}
	password := a.rand.GenerateString(10)
	encryptedPassword := password // 暗号化処理(省略)
	account.Password = encryptedPassword
	if err := a.account.Create(ctx, account); err != nil {
		return "", err
	}
	return password, nil
}
  1. テスト記述
func TestAccount_Create(t *testing.T) {
	t.Run("success", func(t *testing.T) {
		ctx := context.Background()
		randMock := randmock.NewMockGenerator(t)
		// モック化することで、引数に関わらず、返り値を固定できる
		randMock.EXPECT().GenerateString(mock.Anything).Return("1234567890")
		accountMock := persistencemock.NewMockAccount(t)
		accountMock.EXPECT().Create(ctx, mock.Anything).Return(nil)
		a := &Account{
			account: accountMock,
			rand:    randMock,
		}
		got, err := a.Create(ctx, "test@sample.com")
		require.NoError(t, err)
		assert.Equal(t, "1234567890", got)
	})
}
tacomsテックブログ

Discussion