GoMock入門：初心者向けの使い方とテストの書き方

## はじめに

GoMockは、Go言語でモック（Mock）を使ったテストを簡単に書けるようにするライブラリです。  
外部APIやデータベースに依存するコードをテストする際、GoMockを使うことで依存関係を分離し、効率的なユニットテストが実現できます。

本記事では、GoMockの基本的な使い方を中心に、以下の内容を解説します。

**対象読者**
- Go言語のテストを学び始めた方
- 外部APIやDBに依存しないテストを書きたい方
- GoMockを使ってモックを作成したい方

**目次**
1. GoMockとは？
2. インストール方法
3. 基本的な使い方
   - モックの生成
   - モックの設定
   - モックの検証
4. 実践：GoMockを使ったユニットテスト

---

## 1. そもそもGoMockとは？

GoMockは、Go言語用のモック生成ライブラリで、以下の特徴があります。

- 依存関係を分離して、テスト対象の関数やメソッドだけを検証できる。
- gomock.Controllerを使って、モックの動作を設定・検証できる。
- Mockgenというツールを使って、**インターフェースからモックコードを自動生成** できる。

---

## 2. インストール方法

**1. GoMockとMockgenのインストール**
以下のコマンドを実行して、GoMockとMockgenをインストールします。

```sh
go install github.com/golang/mock/mockgen@latest
```

`.zshrc`または`.bash_profile`に以下を追加して、環境変数`PATH`を設定します。
```sh
export PATH=$PATH:$(go env GOPATH)/bin
```

設定を反映させるために、以下を実行します。
```sh
source ~/.zshrc  # Zshを使用している場合
source ~/.bash_profile  # Bashを使用している場合
```

**2. Go Modulesに追加**
`go.mod`にGoMockを追加します。
```sh
go get github.com/golang/mock/gomock
```

---

## 3. 基本的な使い方

**3.1 モックの生成**
Mockgenを使って、インターフェースからモックを自動生成します。  
今回は、`UserRepository`インターフェースをモック化してみます。

**user.go**
```go
package example

type User struct {
    ID   int
    Name string
}

type UserRepository interface {
    GetUser(id int) (*User, error)
}
```

以下のコマンドを実行して、モックを生成します。
```sh
mockgen -source=user.go -destination=mock_user.go -package=example
```

- `-source`: モックを生成する元のインターフェースを含むファイル
- `-destination`: 生成されるモックのファイル名
- `-package`: モックを含むパッケージ名

**3.2 モックの設定**
`gomock.Controller`を使って、モックの動作を設定します。

**user_test.go**
```go
package example_test

import (
    "errors"
    "testing"

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

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

    mockRepo := example.NewMockUserRepository(ctrl)
    mockRepo.EXPECT().GetUser(1).Return(&example.User{ID: 1, Name: "John"}, nil)

    user, err := mockRepo.GetUser(1)
    if err != nil {
        t.Fatal("expected no error, but got", err)
    }
    if user.Name != "John" {
        t.Fatal("expected user name to be John, but got", user.Name)
    }
}
```

**3.3 モックの検証**
- `EXPECT()`を使って、モックの動作を設定
- `Return()`を使って、モックの返り値を設定
- `ctrl.Finish()`で、**呼び出し回数の検証** を行う

---

## 4. 実践：GoMockを使ったユニットテスト

GoMockを使って、**外部依存を分離したユニットテスト**を実装します。  
今回は、`UserService`が`UserRepository`に依存しているケースをテストします。

**4.1 UserServiceの実装**
**user_service.go**
```go
package example

import "errors"

type UserService struct {
    repo UserRepository
}

func NewUserService(repo UserRepository) *UserService {
    return &UserService{repo: repo}
}

func (s *UserService) GetUserName(id int) (string, error) {
    user, err := s.repo.GetUser(id)
    if err != nil {
        return "", err
    }
    if user == nil {
        return "", errors.New("user not found")
    }
    return user.Name, nil
}
```

**4.2 UserServiceのテスト実装**
**user_service_test.go**
```go
package example_test

import (
    "errors"
    "testing"

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

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

    mockRepo := example.NewMockUserRepository(ctrl)
    mockRepo.EXPECT().GetUser(1).Return(&example.User{ID: 1, Name: "John"}, nil)

    service := example.NewUserService(mockRepo)
    name, err := service.GetUserName(1)
    if err != nil {
        t.Fatal("expected no error, but got", err)
    }
    if name != "John" {
        t.Fatal("expected name to be John, but got", name)
    }
}

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

    mockRepo := example.NewMockUserRepository(ctrl)
    mockRepo.EXPECT().GetUser(1).Return(nil, nil)

    service := example.NewUserService(mockRepo)
    _, err := service.GetUserName(1)
    if err == nil || err.Error() != "user not found" {
        t.Fatal("expected user not found error, but got", err)
    }
}
```

- `mockRepo.EXPECT()`を使って、期待する動作を設定
- テストケースとして、成功ケースとユーザーが見つからないケースを実装

---

## まとめ

| 項目 | 説明 |
|------|------|
| **GoMock** | Go言語用のモック生成ライブラリ |
| **Mockgen** | インターフェースからモックを自動生成 |
| **Controller** | モックの動作を設定・検証する |

GoMockを使うことで、外部依存を切り離してテストできるため、高品質なユニットテストを効率的に書くことが可能になります！