0
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

GoMockでmockを自動生成してみる

Last updated at Posted at 2025-10-14

コードをテストするときにmockを使うことが多いと思いますが、goには依存注入したinterfaceのmockを自動生成してくれるライブラリがあります。
その使い方をまとめてみました。

サンプルコード

現在時刻を返してくれるコードです。clockappディレクトリというパッケージ名です。

$ tree
.
├── controller
│   └── controller.go
├── go.mod
├── main.go
└── service
    └── clock.go
go.mod
module clockapp

go 1.24.4

main.go
// main.go

package main

import (
	"clockapp/controller"
	"clockapp/service"
	"fmt"
	"time"
)

func main() {
	loc, err := time.LoadLocation("Asia/Tokyo")
	if err != nil {
		panic(err)
	}

	svc := &service.Clock{Location: loc}
	ctrl := controller.UserController{Clock: svc}
	fmt.Println(ctrl.ResponseCurrentTime())
}

controller/controller.go
// Package controller provides handlers for clock-related operations.
package controller

import "clockapp/service"

type UserController struct {
	Clock service.ClockInterface
}

func (c *UserController) ResponseCurrentTime() string {
	return "It is " + c.Clock.GetCurrentTime() + " now."
}

service/clock.go
// Package service provides clock-related functionality.
package service

import "time"

type ClockInterface interface {
	GetCurrentTime() string
}

type Clock struct {
	Location *time.Location
}

func (r *Clock) GetCurrentTime() string {
	now := time.Now().In(r.Location)
	return now.Format("2006-01-02 15:04:05 MST")
}

これを実行すると例えば

$ go run main.go
It is 2025-10-14 22:48:22 JST now.

のようになります。

テストコード

mockの自動生成

service.Clock.GetCurrentTimeはいつも同じ値を返してくれるわけではないので、controller.UserController.ResponseCurrentTimeのテストには不向きです。そこで、読み込むcontroller.UserController.Clockをmockに置き換えます。

mockgenのインストール

$ go install github.com/golang/mock/mockgen@latest
$ export PATH=$PATH:$(go env GOPATH)/bin

mockパッケージの自動生成

$ mockgen -source=service/clock.go -destination=mocks/clock_mock.go -package=mocks

を実行すればいいのですが、以下のようにコードにコメントを書くとgo generate ./serviceで各ファイルのmockgenを自動で実行してくれます。

service/clock.go
//go:generate mockgen -source=clock.go -destination=../mocks/clock_mock.go -package=mocks

// Package service provides clock-related functionality.
package service

import "time"

type ClockInterface interface {
	GetCurrentTime() string
}

type Clock struct {
	Location *time.Location
}

func (r *Clock) GetCurrentTime() string {
	now := time.Now().In(r.Location)
	return now.Format("2006-01-02 15:04:05 MST")
}

mockのパッケージがimportされるので

go mod tidy

で依存関係を追加しておきます。

ちなみにコードを修正した後にgo generateを実行すれば、mockは修正内容に合わせて上書きされます。

テストコード

clockappパッケージにはservicecontrollerに1個ずつ、構造体があるので、これらをそれぞれテストします。controllerのテストで先ほど作ったmockを使います。

controller/controller_test.go
package controller

import (
	"clockapp/mocks"
	"testing"

	"github.com/golang/mock/gomock"
)

func TestUserController_ResponseCurrentTime(t *testing.T) {
	ctrl := gomock.NewController(t)
	defer ctrl.Finish()

	mockClock := mocks.NewMockClockInterface(ctrl)
	mockClock.EXPECT().GetCurrentTime().Return("2025-10-14 23:59:59 JST")

	uc := UserController{Clock: mockClock}
	got := uc.ResponseCurrentTime()
	want := "It is 2025-10-14 23:59:59 JST now."

	if got != want {
		t.Errorf("expected %q, got %q", want, got)
	}
}
service/clock_test.go
package service

import (
	"testing"
	"time"
)

func TestClock_GetCurrentTime(t *testing.T) {
	loc := time.FixedZone("JST", 9*60*60)
	clock := &Clock{Location: loc}
	got := clock.GetCurrentTime()

	if len(got) != len("2006-01-02 15:04:05 MST") {
		t.Errorf("unexpected format: got %s", got)
	}

	if got[len(got)-3:] != "JST" {
		t.Errorf("expected JST timezone, got %s", got)
	}
}

テストコードを追加するとこんな感じの構成になります。

$ tree
.
├── controller
│   ├── controller.go
│   └── controller_test.go
├── go.mod
├── go.sum
├── main.go
├── mocks
│   └── clock_mock.go
└── service
    ├── clock.go
    └── clock_test.go

実行するとこんな感じです。

$ go test -v ./controller ./service
=== RUN   TestUserController_ResponseCurrentTime
--- PASS: TestUserController_ResponseCurrentTime (0.00s)
PASS
ok      clockapp/controller     (cached)
=== RUN   TestClock_GetCurrentTime
--- PASS: TestClock_GetCurrentTime (0.00s)
PASS
ok      clockapp/service        (cached)

大きなコードになるといちいちmockを手書きで作ったり修正したりするのは面倒なので、こういう機能は便利ですね。

0
0
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
0
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?