Go テスト 2026年版:ユニットテスト、モック、技術面接の質問
テーブル駆動テスト、モック、面接官が期待するパターンでGoのテストをマスターします。testifyとgomockを使った実践的な例を紹介します。

Goのテストは標準のtestingパッケージに依存しており、すべてのGoインストールに同梱され、外部依存関係を必要としません。他の言語のフレームワークとは異なり、Goのアプローチはシンプルさを重視しています。テストファイルはプロダクションコードと同じディレクトリに配置され、テスト関数は命名規則に従い、go testコマンドが検出と実行を処理します。
テーブル駆動テストを書き、依存性注入にインターフェースを使用し、モックが価値を追加する場面とノイズを追加する場面を理解している候補者が評価されます。
testingパッケージを使ったユニットテストの書き方
すべてのテストファイルは_test.goで終わり、テスト対象のコードと同じパッケージに配置されます。テスト関数はTestで始まり、その後に大文字で始まる名前が続きます。*testing.Tパラメータは失敗を報告するためのメソッドを提供します。
package calculator
import "testing"
func TestAdd(t *testing.T) {
result := Add(2, 3)
if result != 5 {
t.Errorf("Add(2, 3) = %d; want 5", result)
}
}go test ./...を実行すると、現在のモジュール内のすべてのテストが実行されます。-vフラグは個々のテスト名とそのパス/失敗ステータスを表示します。
テーブル駆動テスト:Go標準のパターン
テーブル駆動テストは重複を減らし、新しいケースの追加を容易にします。テーブルの各行は、入力と期待される出力を持つ1つのシナリオを表します。このパターンはGo標準ライブラリや業界全体のプロダクションコードベースで見られます。
package validator
import "testing"
func TestValidateEmail(t *testing.T) {
tests := []struct {
name string
email string
wantErr bool
}{
{"valid email", "user@example.com", false},
{"missing at sign", "userexample.com", true},
{"empty string", "", true},
{"unicode local part", "user@例え.jp", false},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
err := ValidateEmail(tt.email)
if (err != nil) != tt.wantErr {
t.Errorf("ValidateEmail(%q) error = %v, wantErr %v",
tt.email, err, tt.wantErr)
}
})
}
}t.Runメソッドは独立して実行されるサブテストを作成します。失敗したサブテストは、他のケースを停止することなく、どの特定のケースが失敗したかを報告します。
インターフェースによる依存性注入
Goのインターフェースは、外部システム(データベース、HTTPクライアント、ファイルシステム)に依存するコードのテストを可能にします。使用箇所で最小限のインターフェースを定義することで、実際の実装をテストダブルに置き換えることができます。
package order
// Repository defines the data access methods this service needs
type Repository interface {
FindByID(id string) (*Order, error)
Save(order *Order) error
}
// Service handles order business logic
type Service struct {
repo Repository
}
// NewService creates a service with the given repository
func NewService(repo Repository) *Service {
return &Service{repo: repo}
}
// Process validates and saves an order
func (s *Service) Process(order *Order) error {
if order.Total <= 0 {
return ErrInvalidTotal
}
return s.repo.Save(order)
}ServiceはRepositoryを満たす任意の型を受け入れます。プロダクションコードはデータベースベースの実装を渡し、テストはモックまたはスタブを渡します。
gomockとmockgenを使ったモック
gomockパッケージはインターフェース定義からモック実装を生成します。mockgenコマンドはソースファイルを読み込み、呼び出しを記録し設定された値を返すモック構造体を生成します。
# Generate mocks for the Repository interface
mockgen -source=service.go -destination=mocks/repository_mock.go -package=mocks生成されたモックをテストで使用する例:
package order
import (
"testing"
"github.com/stretchr/testify/assert"
"go.uber.org/mock/gomock"
"yourmodule/order/mocks"
)
func TestService_Process_SavesValidOrder(t *testing.T) {
ctrl := gomock.NewController(t)
mockRepo := mocks.NewMockRepository(ctrl)
order := &Order{ID: "123", Total: 99.99}
// Expect Save to be called once with this order, return nil
mockRepo.EXPECT().
Save(order).
Return(nil).
Times(1)
svc := NewService(mockRepo)
err := svc.Process(order)
assert.NoError(t, err)
}
func TestService_Process_RejectsZeroTotal(t *testing.T) {
ctrl := gomock.NewController(t)
mockRepo := mocks.NewMockRepository(ctrl)
// No calls expected to repo since validation fails first
svc := NewService(mockRepo)
err := svc.Process(&Order{ID: "456", Total: 0})
assert.ErrorIs(t, err, ErrInvalidTotal)
}モックは、Saveが期待される引数で正確に1回呼び出されたことを検証します。テスト対象のコードがSaveを呼び出さない場合、または異なる引数で呼び出した場合、テストは失敗します。
testifyを使ったアサーションとスイート
testifyは、手動のif文チェックよりも明確な失敗メッセージを生成するアサーション関数を提供します。assertパッケージは失敗後も実行を継続します。requireパッケージはテストを即座に停止します。
package user
import (
"testing"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
)
func TestParseUser(t *testing.T) {
input := `{"id": "u1", "name": "Alice"}`
user, err := ParseUser(input)
require.NoError(t, err, "parsing should not fail")
assert.Equal(t, "u1", user.ID)
assert.Equal(t, "Alice", user.Name)
assert.Empty(t, user.Email, "email should default to empty")
}require.NoError呼び出しは、パースが失敗した場合にテストを停止し、後続のアサーションでのnilポインタパニックを防ぎます。
Goの面接対策はできていますか?
インタラクティブなシミュレーター、flashcards、技術テストで練習しましょう。
httptestを使ったHTTPハンドラのテスト
net/http/httptestパッケージは、ハンドラ出力をキャプチャするためのResponseRecorderと、ローカルテストサーバーを実行するためのServerを提供します。ほとんどのユニットテストは、ネットワークオーバーヘッドを回避するためにResponseRecorderを使用します。
package api
import (
"net/http"
"net/http/httptest"
"testing"
"github.com/stretchr/testify/assert"
)
func TestHealthHandler(t *testing.T) {
req := httptest.NewRequest(http.MethodGet, "/health", nil)
rec := httptest.NewRecorder()
HealthHandler(rec, req)
assert.Equal(t, http.StatusOK, rec.Code)
assert.JSONEq(t, `{"status": "ok"}`, rec.Body.String())
}外部サービスを呼び出すハンドラの場合は、モッククライアントを注入するか、httptest.NewServerを使用してフェイクバックエンドを作成します。
Goテストに関するよくある面接質問
面接官は、実際のコードベースでの経験を明らかにする質問を通じてテストの知識を評価します。準備の整った候補者を区別するパターンは以下の通りです:
「外部APIを呼び出す関数をどのようにテストしますか?」
HTTPクライアント用のインターフェースを定義し、関数または構造体に注入し、事前に決められたレスポンスを返すモックを提供します。これによりネットワーク呼び出しを回避し、テストを決定論的にします。
「スタブとモックをいつ使い分けますか?」
スタブは、呼び出され方を検証せずに定型のレスポンスを返します。モックは、特定のメソッドが特定の引数で呼び出されたことを検証します。戻り値がインタラクションよりも重要な場合はスタブを使用します。インタラクションの検証がテストの目的である場合はモックを使用します。
「テーブル駆動テストとは何ですか?なぜGoはそれを好むのですか?」
テーブル駆動テストは、テストケースを構造体のスライスのデータとして定義します。ボイラープレートを削減し、ケースの追加を容易にし、t.Runで読みやすいテスト出力を生成します。このパターンは、巧妙な抽象化よりも明示的で反復的なコードを好むGoの哲学に合致しています。
「複数のテストが共有するテストフィクスチャやセットアップをどのように処理しますか?」
パッケージレベルのセットアップとティアダウンにはTestMainを使用します。関連するテスト間で共有されるセットアップには、ヘルパー関数またはテストスイート(testifyから)を使用します。テスト間の結合を生み出すグローバル状態を避けます。
Goの面接準備をしていますか?Goテストモジュールでは、これらのパターンを練習問題とともに詳しく説明しています。
カバレッジとレース検出を使ったテスト実行
go testコマンドは、未テストのコードパスと並行性のバグを明らかにするフラグを受け入れます。
# Run tests with coverage report
go test -cover ./...
# Generate HTML coverage visualization
go test -coverprofile=coverage.out ./...
go tool cover -html=coverage.out -o coverage.html
# Detect race conditions (slower, but catches real bugs)
go test -race ./...レース検出器はメモリアクセスを計装し、同じ変数への並行読み書きをフラグ付けします。CIで-raceフラグを付けてテストを実行すると、特定のタイミングでのみ発生するバグをキャッチできます。
大規模コードベースでのテスト整理
プロジェクトが成長するにつれて、テストの整理は保守性に影響を与えます:
- 同一パッケージテスト(
package foo)は非公開関数やフィールドにアクセスできます。内部動作を検証するユニットテストに使用します。 - 外部パッケージテスト(
package foo_test)は外部コードのようにパッケージをインポートします。統合テストや公開APIの検証に使用します。 - testdataディレクトリはJSONファイル、ゴールデン出力、シードデータベースなどのフィクスチャを保存します。Goはビルド時にこれらを無視します。
- ビルドタグは外部システムを必要とする統合テストからユニットテストを分離します。
go test -tags=integration ./...を実行してそれらを含めます。
テスト以外のGoベストプラクティスについては、並行性パターンの記事でゴルーチンとチャネルについて説明しています。
Goテストで覚えておくべきこと
testingパッケージはGoに同梱されており、外部依存関係を必要としません- テーブル駆動テストは重複を減らし、面接で期待されるパターンです
- インターフェースは依存性注入を可能にします。実装箇所ではなく使用箇所で定義します
gomockはインターフェースからモックを生成します。testifyはより明確なアサーションを提供しますhttptest.ResponseRecorderはネットワークオーバーヘッドなしでハンドラ出力をキャプチャします-raceフラグはユニットテストでは見逃される並行性のバグを検出します- カバレッジメトリクスは注意を導きますが、正確性を保証するものではありません
今すぐ練習を始めましょう!
面接シミュレーターと技術テストで知識をテストしましょう。
Go のバグを見つけられますか
実際のコード、隠れたバグ、1日1回。アカウントなしで試せます。

執筆
Anthony Fillion-MailletSharpSkill 創業者
10 年以上フルスタック開発に携わっています。SharpSkill を運営し、ここで公開される内容に責任を負っています。
2026年8月24日 更新
タグ
共有
関連記事

Go Context パッケージ 2026年完全ガイド:キャンセル、タイムアウト、面接対策
Go言語のcontextパッケージを徹底解説。キャンセル処理、タイムアウト設定、リクエストスコープ値の活用法から、技術面接で頻出の質問と回答まで、実践的なコード例とともに解説します。

Go面接の頻出25問: 開発者向け完全ガイド
頻出25問でGoの面接を制する。ゴルーチン、チャネル、インターフェース、並行処理パターンをコード例とともに解説。

Go技術面接:Goroutine、Channel、並行処理パターン完全ガイド
Go技術面接で頻出のgoroutine、channel、並行処理に関する質問を網羅。本番レベルのコード例と各回答の背景にある設計思想を2026年の面接対策として解説します。