NeovimでGoテストを爆速生成!gogentestプラグインで開発効率UP
はじめに
Goのテストコードを書くとき、こんな悩みはありませんか?
- テーブルドリブンテストのボイラープレートを毎回手書きするのが面倒
- 関数のシグネチャから引数の型を正確にコピーするのが手間
-
assert.ErrorAssertionFunc
の使い方を毎回調べてしまう
そんな悩みを解決するNeovimプラグイン「gogentest」を開発しました。LSP(gopls)を活用して、カーソル位置の関数から型情報を含む完全なテストテンプレートを自動生成します。
gogentestとは
gogentestは、Goの関数からテストテンプレートを自動生成するNeovimプラグインです。
主な特徴
- LSP(gopls)を使用して正確な型情報を抽出
- Goland互換のテーブルドリブンテストを生成
- goplsが使えない場合はTreesitterにフォールバック
- ワンコマンドでテストファイルの作成から実装まで
生成されるテストの例
例えば、以下のような関数があったとします:
func ProcessData(ctx context.Context, id int, data string) (string, error) {
if id <= 0 {
return "", errors.New("invalid id")
}
if data == "" {
return "", errors.New("empty data")
}
return fmt.Sprintf("processed: %s (id: %d)", data, id), nil
}
gogentestを実行すると、以下のようなテストが自動生成されます:
package mypackage_test
import (
"context"
"fmt"
"testing"
"github.com/stretchr/testify/assert"
)
func TestProcessData(t *testing.T) {
type args struct {
ctx context.Context
id int
data string
}
tests := []struct {
name string
args args
want string
wantErr assert.ErrorAssertionFunc
}{
// TODO add cases
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
got, err := ProcessData(tt.args.ctx, tt.args.id, tt.args.data)
if !tt.wantErr(t, err, fmt.Sprintf("ProcessData(%v, %v, %v)", tt.args.ctx, tt.args.id, tt.args.data)) {
return
}
assert.Equalf(t, tt.want, got, "ProcessData(%v, %v, %v)", tt.args.ctx, tt.args.id, tt.args.data)
})
}
}
型情報が正確に反映され、エラーハンドリングも適切に設定されているのがわかります。
導入方法
前提条件
- Neovim 0.8以上
- gopls(通常はGo開発環境と一緒にインストールされる)
- オプション:nvim-treesitter(フォールバック用)
インストール
lazy.nvimを使用する場合
return {
{
"YuminosukeSato/gogentest",
ft = "go",
dependencies = { "neovim/nvim-lspconfig" },
keys = {
{ "<leader>tG", function() require("gogentest").generate() end, desc = "Generate Go Test" },
},
},
}
LazyVimを使用する場合
~/.config/nvim/lua/plugins/gogentest.lua
を作成:
return {
{
"YuminosukeSato/gogentest",
ft = "go",
dependencies = { "neovim/nvim-lspconfig" },
keys = {
{ "<leader>tG", function() require("gogentest").generate() end, desc = "Generate Go Test" },
},
},
-- goplsの設定も含める
{
"neovim/nvim-lspconfig",
opts = {
servers = {
gopls = {
settings = {
gopls = {
analyses = {
unusedparams = true,
},
staticcheck = true,
},
},
},
},
},
},
}
基本的な使い方(ハンズオン)
では、実際にgogentestを使ってみましょう。
Step 1: サンプル関数を作成
calculator.go
という名前で以下のファイルを作る:
package calculator
import (
"errors"
)
// Add は2つの整数を加算する
func Add(a, b int) int {
return a + b
}
// Divide は除算を行い、ゼロ除算の場合はエラーを返す
func Divide(a, b float64) (float64, error) {
if b == 0 {
return 0, errors.New("division by zero")
}
return a / b, nil
}
Step 2: テストを生成
- Neovimで
calculator.go
を開く -
Add
関数の上にカーソルを置く -
<leader>tG
(デフォルトのキーバインド)を押すか、:GogentestGenerate
を実行する
すると、calculator_test.go
が自動的に作成され、以下のようなテストが生成される:
package calculator_test
import (
"fmt"
"testing"
"github.com/stretchr/testify/assert"
)
func TestAdd(t *testing.T) {
type args struct {
a int
b int
}
tests := []struct {
name string
args args
want int
}{
// TODO add cases
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
got := Add(tt.args.a, tt.args.b)
assert.Equalf(t, tt.want, got, "Add(%v, %v)", tt.args.a, tt.args.b)
})
}
}
Step 3: テストケースを追加
生成されたテンプレートに、実際のテストケースを追加する:
tests := []struct {
name string
args args
want int
}{
{
name: "正の数の加算",
args: args{a: 2, b: 3},
want: 5,
},
{
name: "負の数を含む加算",
args: args{a: -1, b: 1},
want: 0,
},
{
name: "ゼロの加算",
args: args{a: 0, b: 0},
want: 0,
},
}
発展的な使い方
エラーを返す関数のテスト
Divide
関数のようにエラーを返す関数の場合、gogentestはassert.ErrorAssertionFunc
を使用したテンプレートを生成する:
func TestDivide(t *testing.T) {
type args struct {
a float64
b float64
}
tests := []struct {
name string
args args
want float64
wantErr assert.ErrorAssertionFunc
}{
{
name: "正常な除算",
args: args{a: 10, b: 2},
want: 5,
wantErr: assert.NoError,
},
{
name: "ゼロ除算",
args: args{a: 10, b: 0},
want: 0,
wantErr: assert.Error,
},
}
// ...
}
カスタムキーバインド
プロジェクトに応じて、キーバインドをカスタマイズできる:
vim.api.nvim_create_autocmd("FileType", {
pattern = "go",
callback = function()
vim.keymap.set("n", "<leader>gt", function()
require("gogentest").generate()
end, { buffer = true, desc = "Generate Go test" })
end,
})
類似ツールとの比較
gotestsとの違い
gotests
は有名なGoテスト生成ツールだが、gogentestには以下の利点がある:
機能 | gogentest | gotests |
---|---|---|
Neovim統合 | ネイティブ | 外部コマンド |
LSP活用 | ✅ | ❌ |
リアルタイム型情報 | ✅ | ❌ |
Treesitterフォールバック | ✅ | ❌ |
testify/assert対応 | デフォルト | オプション |
gogentestは、Neovimのエコシステムに統合されており、編集中のコンテキストを活用してより正確なテストを生成できる。
内部実装の仕組み
gogentestは以下のような流れで動作する:
-
LSPクエリ: カーソル位置で
textDocument/signatureHelp
を送信 - シグネチャ解析: 関数名、引数、戻り値を抽出
- テンプレート生成: 型情報をもとに構造体とアサーションを生成
- フォールバック: LSPが使えない場合はTreesitterで関数名のみ取得
この仕組みにより、常に最新の型情報を反映したテストを生成できる。
トラブルシューティング
"gopls unavailable"エラー
goplsがインストールされていることを確認:
go install golang.org/x/tools/gopls@latest
その後、Neovimを再起動するか:LspRestart
を実行。
型情報が取得できない
以下を確認:
- Goファイルに構文エラーがないか
-
go mod init
でモジュールが初期化されているか - goplsが正しく設定されているか(
:LspInfo
で確認)
まとめ
gogentestを使うことで、Goのテストコード作成が効率化される。特に:
- ボイラープレートの手書きが不要
- 型情報の正確な反映
- エラーハンドリングが適切に実装される
- Neovimとのシームレスな統合
ぜひ、日々のGo開発に取り入れて、テスト作成の時間を削減し、より価値のあるコード実装に時間を使ってください。
リンク
Happy Testing! 🎉
Discussion