claude codeのVS Code拡張がインストールされない問題への対応
2025/6/24追記
下記の記事で知ったのですが、どうやら今はVSCode上から検索可能になった、とのことでした。 https://dev.classmethod.jp/articles/claude-code-vscode-extension-review/
概要
とうとうclaude codeをセットアップして、本格的にAIコーディングを試してみようとした。 公式サイトや初心者向けのyoutube動画などを見ながらいろいろと試しはじめたが、VSCode拡張のインストールに手こずってしまったので、その時の記録。
症状
VS-Code上で/ideコマンドを起動しても対象となるIDEが見つからない、というメッセージが表示され、その以上は何もできない状態だった。

対応方法
結論はこちらの記事のとおり、claude codeのnpmインストールディレクトリ上のvenderディレクトリにvscodeの拡張機能ファイルが置いてあるのでこのディレクトリまで移動して手動で格納機能をインストールすることで対応できた。
❯ code --install-extension claude-code.vsix Installing extensions... Extension 'claude-code.vsix' was successfully installed.
しかし、最初、そもそもclaude-codeのnpmがどこにインストールされているかがわからない、という問題に直面した。 主要なプログラム言語などをasdfで管理していたので~/.asdf以下だろうとは考えて、適当なディレクトリを探ってみたが、見つからない。
仕方がないのでclaudeに質問したら、すぐにわかった。
> npxコマンドをグローバルインストールしましたが、インストール先がわかりません。インストール先を調べる方法を教えてください。
● Bash(npm list -g --depth=0)
⎿ ~/.asdf/installs/nodejs/23.11.1/.npm/lib
└── @anthropic-ai/claude-code@1.0.24
● Bash(npm root -g)
⎿ ~/.asdf/installs/nodejs/23.11.1/.npm/lib/node_modules
● グローバルインストール先は ~/.asdf/installs/nodejs/23.11.1/.npm/lib/node_modules です。
調べる方法:
- npm root -g: グローバルインストールディレクトリを表示
- npm list -g --depth=0: グローバルにインストールされたパッケージ一覧を表示
もちろん、上の回答にあるとおり、npm root -gでインストール先を知ることができる。
❯ npm root -g ~/.asdf/installs/nodejs/23.11.1/.npm/lib/node_modules
インストール先がわかったのであとはVS拡張機能のインストールのみ。
cd ~/.asdf/installs/nodejs/23.11.1/.npm/lib/node_modules cd @anthropic-ai/claude-code/vendor code --install-extension claude-code.vsix
そして再度、VSCodeを再度起動してVSCode内のターミナルでideコマンドを実行。
今度は無事に連携できた。 良かった。

go-dynoについて調べてみた
きっかけ
技術書典で購入した、「SGE Go Tech Book Vol.07」という下記の本でgo-dynoの存在を知り、面白そうだったので軽く調べてみた。
概要
go-dynoは動的にメソッド呼び出しをproxyしてくれるライブラリ。どうやらJavaの動的proxy機構をgoで実現したくて作った模様。 横断的な処理を任意のinterfaceやstructに対して追加し、機能追加することが可能になる。
dynoが提供するproxyにより、Handleメソッド内で定義された内容を実行して、任意のタイミングでオリジナルのメソッドが実行されるようにできる。 イメージとしてはserverのmiddlewareと同じようなことを任意のinterfaceやstructに対して実施できるようになる。
こちらのツイートを見るとどうやらモックライブラリのmockioと同じ作者でmockio内部で使用されている、とのこと。
これの裏で使われているgo-dynoが、動的にコード生成なしでメソッド実装していてすごかったhttps://t.co/SbzBfVrkoF https://t.co/19XHERXVeU
— kadota (@plan9user) 2025年4月7日
使い方
公式のサンプルとほぼ同じだが、少しコメントなどをつけ加えている。
今回の場合であれば、Greet1とGreet2の2つのメソッドが実行される前後で、それぞれ”Before〜"と"After〜"が表示されている。
main.go
package main import ( "fmt" "reflect" "github.com/ovechkin-dm/go-dyno/pkg/dyno" ) // target interface type Greeter interface { Greet1() Greet2() } // target type SimpleGreeter struct{} func (g *SimpleGreeter) Greet1() { fmt.Println("Hello1!") } func (g *SimpleGreeter) Greet2() { fmt.Println("Hello2!") } // proxy type ProxyHandler[T any] struct { Impl T } func (p *ProxyHandler[T]) Handle(m reflect.Method, values []reflect.Value) []reflect.Value { fmt.Println("Before process: method call:", m.Name) ret := reflect.ValueOf(p.Impl).MethodByName(m.Name).Call(values) fmt.Println("After process: method called:", m.Name) return ret } func NewDynamicGreeter() Greeter { greeter := &SimpleGreeter{} proxyHandler := &ProxyHandler[Greeter]{Impl: greeter} dynamicGreeter, _ := dyno.Dynamic[Greeter](proxyHandler.Handle) return dynamicGreeter } func main() { dynamicGreeter := NewDynamicGreeter() dynamicGreeter.Greet1() dynamicGreeter.Greet2() } // 結果 // Before process: method call: Greet1 // Hello1! // After process: method called: Greet1 // Before process: method call: Greet2 // Hello2! // After process: method called: Greet2
これを使うことで横断的に処理したい内容(例えばロギングとかOpenTelemetolyのスパンをとか)を元の処理には手を加えず、コンストラクタに手を加えるだけで差し挟むことができるようになる。
ただ、メソッド呼び出しだけとはいえ、リフレクションでの呼び出しになるのでその分、パフォーマンスは犠牲になる。 犠牲になるのはどのぐらいか? 調べてみた。
ベンチマーク
最初のプログラムをさらに改造してベンチマークに適した形にし、ベンチマーク用のコードを追加し、性能を検証してみた。
修正内容は以下のとおり。 * ベンチマークには邪魔になるのでfmt.Printlnでの表示を削除し、Greet関数は文字列を返却するだけに修正 * NewDynamicGreeter関数ではgo-dynoのproxy経由でのインスタンスを提供 * NewStaticGreeter関数ではgo-dynoを経由しないインスタンスを提供 * ベンチマーク用のbenchmark_test.goを追加
main.go
package main import ( "reflect" "github.com/ovechkin-dm/go-dyno/pkg/dyno" ) // target interface type Greeter interface { Greet() string } // target type SimpleGreeter struct{} func (g *SimpleGreeter) Greet() string { return "Hello!" } // proxy type ProxyHandler[T any] struct { Impl T } func (p *ProxyHandler[T]) Handle(m reflect.Method, values []reflect.Value) []reflect.Value { return reflect.ValueOf(p.Impl).MethodByName(m.Name).Call(values) } func NewDynamicGreeter() Greeter { greeter := &SimpleGreeter{} proxyHandler := &ProxyHandler[Greeter]{Impl: greeter} dynamicGreeter, _ := dyno.Dynamic[Greeter](proxyHandler.Handle) return dynamicGreeter } func NewStaticGreeter() Greeter { return &SimpleGreeter{} } func main() { dynamicGreeter := NewDynamicGreeter() dynamicGreeter.Greet() }
benchmark_test.go
package main import "testing" func BenchmarkToDynamicGreeter(b *testing.B) { greeter := NewDynamicGreeter() b.ResetTimer() for i := 0; i < b.N; i++ { greeter.Greet() } } func BenchmarkToStaticGreeter(b *testing.B) { greeter := NewStaticGreeter() b.ResetTimer() for i := 0; i < b.N; i++ { greeter.Greet() } }
ベンチマーク結果
一番、右側が1回呼び出し辺りの処理時間。
go-dynoを使った方を平均650ns、使わない方を平均0.25nsとすると約2600倍、go-dynoを使った方が遅いということになる(雑なベンチだけども・・・)
❯ go test -bench=. -benchtime=10000x -count=3 goos: linux goarch: amd64 pkg: github.com/miyazi777/go-dyno-sample1 cpu: 11th Gen Intel(R) Core(TM) i7-11700K @ 3.60GHz BenchmarkToDynamicGreeter-16 10000 624.0 ns/op BenchmarkToDynamicGreeter-16 10000 670.2 ns/op BenchmarkToDynamicGreeter-16 10000 645.5 ns/op BenchmarkToStaticGreeter-16 10000 0.2243 ns/op BenchmarkToStaticGreeter-16 10000 0.2302 ns/op BenchmarkToStaticGreeter-16 10000 0.2295 ns/op PASS ok github.com/miyazi777/go-dyno-sample1 0.024s
go-dynoを使った方が遅いだろうな、というのは予想どおりではあるものの、想像より差があった。
まとめ
横断的な処理を差し挟みたい場合は以下のように判断して良さそう。
- テストコードでの使用、ツールでの使用、そこまで処理性能を重視していない・上記のようなオーバーヘッドが許容できるのであれば、go-dynoは良い選択肢になると思う
- 処理性能を重視したい場合で横断的な処理を挟みたい場合はgo-wrapなどコード生成で対応するのが良さそう
Go言語でテストケースからだけ呼び出しされている処理はビルドファイルに含まれるのか?
概要
Goで開発をしていて、どこまでがビルド対象なのか? が気になったので調査した。
テストコードの_testのサフィックスが付いたファイルはビルド対象にはならない、ということはなんとなく知っていたが、それ以外は?
例えば、gomockという有名なモックライブラリがあるが、このライブラリはモックをinterfaceから自動生成する機能を持っている。 自動生成されたモックは当然テスト時にしか使用しない。
これらはビルドに含まれるのか?
結論
アプリケーションから呼び出しがあるもの以外は含まれない、が結論になる
検証
テストコードからしか参照されていないケース
検証用コードの作成
今回の検証ではtest_util.goに定義されている定数TestStrがビルド対象となるかを検証していきます。
test_util.goのTestStrはmain_test.goからしか参照されていないコードを作成します。
ファイル構成
main.go main_test.go testutil/test_util.go
main.go
package main func main() { fmt.Println("main") }
main_test.go
package main func Test(t *testing.T) { fmt.Println(testutil.TestStr) }
testutil/test_util.go
package testutil var TestStr = "testutil"
検証
検証にはnmというコマンドを使用。
これはオブジェクト内のシンボルを表示することができるツールで、今回の検証にはうってつけ。
以下のようにgoの実行ファイルを作成後、nmコマンドで実行して検証。
> go build -o bin > nm bin | grep testutil >
testutilがビルドに含まれているのであれば、シンボルとしてtestutilが表示されるハズだが、今回は表示されていないので、このバイナリには含まれていないことがわかった。
アプケーションコードとテストコードの両方から参照されているケース
検証用のコードの修正
main.goからtest_util.goの定数を参照するように修正。
main.go
package main func main() { fmt.Println("main") fmt.Println(testutil.TestStr) }
検証
同じようにビルド後にnmコマンドを実行する。
> go build -o bin > nm bin | grep testutil 000000000055afe0 D test1/testutil.TestStr
今回はシンボルとしてtestutilの定数が含まれていることがわかった。 つまりアプリケーションとして必要だから定数に含まれるようになった。
コンパイラを作る人たちには当たり前の話しかもしれないが、こうして検証してみるといろいろとわかって楽しい。
参考
今回のことはほぼ、以下のstack overflowに記載されていたことを少し修正して試したが、大変参考になった。
GoでのAOP(aspect oriented programming)的なことを実現する方法
概要
タイトルどおり、Go言語でAOP(aspect oriented programming)的なことを実現する方法を考えてみました。 JavaでSpringフレームワークで実現されているAOPやpythonのデコレータをイメージしていただけたらと思います。
具体的な実現方法
思いついた方法は以下の3つでした。
- ラッパー関数で実現
- リフレクションで実現
- ツールで実現
以下で処理の前後にログを差し挟む、という処理を実現する方法を検証してみました。
ラッパー関数で実現
機能を付与したい構造体と同じinterfaceを満たす実装で実現する。 一番シンプルだが、機能を付与したい構造体の全てのinterfaceを満たすのは面倒であまり実用的でないように思う。
package main import "fmt" func main() { w := WrapProcess(NewProcess()) w.Execute() // Output: // Before // Executing process... // After } // 実現したい処理 type Process interface { Execcute() } func NewProcess() Process { return &process{} } type process struct{} func (p *process) Execcute() { fmt.Println("Executing process...") } // wrapper type Wrapper struct { wrappedProcess Process } func WrapProcess(p Process) Wrapper { return Wrapper{ wrappedProcess: p, } } func (w *Wrapper) Execute() { fmt.Println("Before") w.wrappedProcess.Execcute() fmt.Println("After") }
リフレクションで実現
リフレクションを使用することで上記のラッパー関数を用意する方法に比べたら、どこにでも適用しやすい分実用的だとは思う。 少なくともpythonとデコレータ相当のことはこれで実現できそう。 ただし、リフレクションを使用しているので毎回呼び出される場合のオーバーヘッドが許容できるかどうかは作成するアプリによる。
package main import ( "fmt" "reflect" ) func wrap[T any](fn T) T { rfn := reflect.ValueOf(fn) // 関数でない場合はpanic if rfn.Kind() != reflect.Func { panic("fn should be a function") } // 同じ引数・戻り値を持つ関数を作成 wfn := reflect.MakeFunc(rfn.Type(), func(args []reflect.Value) []reflect.Value { fmt.Println("before") result := rfn.Call(args) fmt.Println("after") return result }) return wfn.Interface().(T) } func main() { wrap(display)() // こんにちは、世界! wrap(greet)("山田") // こんにちは、山田さん! wrapedAdd := wrap(add) fmt.Println("result", wrapedAdd(5, 3)) // 8 } func display() { fmt.Println("こんにちは、世界!") } func greet(name string) { fmt.Printf("こんにちは、%sさん!\n", name) } func add(a, b int) int { return a + b }
参考にした記事
ツールで実現
ライブラリやツールがないかと探し出したところ、gowrapというコード生成ツールがあり、使えそうだった。
このツールは横断的に適用したい処理をテンプレートとして記述し、適用対象のinterfaceをCLIツールで指定してコード生成するツール。 詳しい使い方は以下に記述。
使った感想としては本格的なアプリを作る場合、上記の2つの手段に比べて一番実用的に感じた。 ただ、最初にテンプレート用の独自の関数を知っておく必要があるが、それについては特にキチンとしたドキュメントが用意されているわけではない為、最初はテンプレート作成に苦労するかも。
使い方
インストール
CLIとしていストールしたかったので下記のコマンドを実行。
go install github.com/hexdigest/gowrap/cmd/gowrap@latest
テンプレート作成
gowrapにはすでにいくつかのテンプレートが作成されていたので、それを元に下記のようなテンプレートを作成。 元々の処理の呼び出し前後で"Before"と"After"を出力するだけのシンプルな処理のテンプレート。
./gowrap/log_template.txt
import (
"io"
"fmt"
)
{{ $decorator := (or .Vars.DecoratorName (printf "%sWithLog" .Interface.Name)) }}
// {{$decorator}} implements {{.Interface.Type}} that is instrumented with logging
type {{$decorator}} struct {
_base {{.Interface.Type}}
}
// New{{$decorator}} instruments an implementation of the {{.Interface.Type}} with simple logging
func New{{$decorator}}(base {{.Interface.Type}}, stdout, stderr io.Writer) {{$decorator}} {
return {{$decorator}}{
_base: base,
}
}
{{range $method := .Interface.Methods}}
// {{$method.Name}} implements {{$.Interface.Type}}
func (_d {{$decorator}}) {{$method.Declaration}} {
fmt.Println("Before")
{{- if $method.HasResults}}
{{$method.ResultsNames}} = _d._base.{{$method.Call}}
fmt.Println("After")
return {{$method.ResultsNames}}
{{else}}
_d._base.{{$method.Call}}
fmt.Println("After")
{{end}}
}
{{end}}
テンプレートは正直、慣れないとわかりづらい部分があると思うが、以下のページと既存テンプレートを見ることでなんとなくわかると思う。
生成元になるinterfaceを定義
./gowrap/main.go
package main type Process interface { Execute1() Execute2() string }
テンプレートとinterfaceを元にコード生成
以下のコマンドでいよいよコード生成する。
gowrap gen -p ./gowrap -i Process -t ./gowrap/log_template.txt -o ./gowrap/log_process.go
できたコードは以下のようになる。
// Code generated by gowrap. DO NOT EDIT. // template: log_template.txt // gowrap: http://github.com/hexdigest/gowrap package main //go:generate gowrap gen -p github.com/miyazi777/test1/gowrap -i Process -t log_template.txt -o log_process.go -l "" import ( "fmt" "io" ) // ProcessWithLog implements Process that is instrumented with logging type ProcessWithLog struct { _base Process } // NewProcessWithLog instruments an implementation of the Process with simple logging func NewProcessWithLog(base Process, stdout, stderr io.Writer) ProcessWithLog { return ProcessWithLog{ _base: base, } } // Execute1 implements Process func (_d ProcessWithLog) Execute1() { fmt.Println("Before") _d._base.Execute1() fmt.Println("After") } // Execute2 implements Process func (_d ProcessWithLog) Execute2() (s1 string) { fmt.Println("Before") s1 = _d._base.Execute2() fmt.Println("After") return s1 }
参考
gowrapを使う際に参考した記事。
その他
実をいうともう1つ方法があるにはある。 コンパイル時に-toolexecオプションを使用することでも同様のことは実現できるっぽい。 実現方法も一番スマートだと思う。 こんなライブラリも存在するぐらい。
ただし、-toolexecオプション自体に関する情報がなさすぎて、これを使っていいのかどうか。 個人的なプロジェクトで使用する分には全然良いけど、商用のプロダクションコードで使用するには躊躇するぐらい情報がなかった。
goverterを使って構造体間の詰替えが可能か検証
概要
Goでアプリケーションを開発していると構造体から構造体への詰替えは頻繁に発生すると思います。 特に項目数が多いと単調なコードを繰り返し記述することになり、微妙にしんどいなと感じています。
最近は生成AIがあるので多少はマシになったとはいえ、全てケースで正確に生成されるわけでもない為、決定的な解決策ではないと感じています。
この辺り、何か楽できるものはないかと思って調べてみました。 その中でgoverterというツールを見つけたので検証してみました。
公式サイト
githubはこちら。
大雑把には変換したい2つの構造体を入力と出力に指定した関数を持つinterfaceを定義すると、そこから詰替え処理コードを生成してくれるツールになります。
使い方
以下の3ステップで使えるようになります。
1. インストール
まずは以下のようにinstallコマンドでinstallします。
go install github.com/jmattheis/goverter/cmd/goverter@v1.8.3
2. 変換処理のinterfaceを定義
次にこのツールのユニークな所だと思いますが、変換処理のinterfaceを定義します。 さらに定義したinterfaceにgoverter用のコメントを付与することで、goverterでの変換処理用のinterfaceであることを明示します。
具体的には以下のようなコードを記述することでInput1からOutput1へ変換処理をConverter1というinterfaceを満たす実装を生成します。
sample/sample1.go
package sample type Input1 struct { Name string Age int } type Oupput1 struct { Name string Age int } // goverter:converter type Converter1 interface { Convert(source Input1) Oupput1 }
3. 詰替え処理を生成
コマンドラインから以下のコマンドを実行します。
goverter gen ./sample/.
特に出力先などを指定しない場合、interfaceを定義したファイル直下にgeneratedというディレクトリが作成され、その中にgenerated.goというファイルが生成されます。 中身はこのようになっています。
sample/generated/generated.go
package generated import sample "github.com/miyazi777/sample1/sample" type Converter1Impl struct{} func (c *Converter1Impl) Convert(source sample.Input1) sample.Oupput1 { var sampleOupput1 sample.Oupput1 sampleOupput1.Name = source.Name sampleOupput1.Age = source.Age return sampleOupput1 }
Input1構造体のNameとAgeの詰替え処理を生成されています。
検証
ここからはいくつか、ありそうなシチュエーションを検証してみました。
無視したい項目がある時
項目の無視は可能。
入力側で無視したい項目がある場合は特に設定は不要。
出力側で無視したい項目がある場合は以下のサンプルのようにgoverter:ignore <項目名>のコメントを追記することで無視することが可能
以下のサンプルではInput2のGenderは無視され、Output2のAddressがignore指定されている為、無視されます。
package sample type Input2 struct { Name string Age int Gender int } type Oupput2 struct { Name string Age int Address string } // goverter:converter type Converter2 interface { // goverter:ignore Address Convert(source Input2) Oupput2 }
入力と出力で項目名が違う時
マッピング可能。
goverter:map <入力項目名> <出力項目名>を指定します。
以下サンプル。 下のサンプルではName -> UserNameに名前が違う項目への詰替えが実施されるコードが生成されました。
package sample type Input3 struct { Name string Age int } type Oupput3 struct { UserName string Age int } // goverter:converter type Converter3 interface { // goverter:map Name UserName Convert(source Input3) Oupput3 }
型が違う項目を変換したい時
マッピング可能。
goverter:map <入力項目名> <出力項目名> | <変換関数名>を指定する。
これは下のサンプルを見てもらった方がわかりやすいと思います。 int型のAgeからstring型のAgeに変換していて、変換関数にはConvertAgeを指定。
package sample import "strconv" type Input4 struct { Name string Age int } type Oupput4 struct { Name string Age string } // goverter:converter type Converter4 interface { // goverter:map Age Age | ConvertAge Convert(source Input4) Oupput4 } func ConvertAge(age int) string { return strconv.Itoa(age) }
ネストした構造体を変換したい時
親子、それぞれの構造体を指定することで可能。
これもサンプルを見てもらった方がわかりやすいと思います。 Input5とOutput5、それぞれにDetailというネストした構造体が存在しています。 そして、親の変換処理である、Converter5と子の変換処理である、DetailConverter5を定義することで対応可能となります。
package sample type Input5 struct { Name string Age int Detail Input5Detail } type Input5Detail struct { DetailName string DetailType int } type Output5 struct { Name string Age int Detail Output5Detail } type Output5Detail struct { DetailName string DetailType int } // goverter:converter type Converter5 interface { Convert(source Input5) Output5 } // goverter:converter type DetailConverter5 interface { Convert(source Input5Detail) Output5Detail }
非公開フィールドを持つ構造体
残念ながら対応していませんでした。
公式ページには非公開フィールドに関するページはあるのですが、これを読む限りは無視するか、コンストラクタがあるのであればコンストラクタを使用する、ということが案内されており、このツールで対応しないようでした。 https://goverter.jmattheis.de/guide/unexported-field
またissueでもgetterやsetterをサポートについての議論もあったのですが、特にこれがツールに反映されている様子はありませんでした。 https://github.com/jmattheis/goverter/issues/179
非公開フィールドへの対応まであったら、さらに使える幅が広がりそうなのに・・・
最後に
詰替え処理を楽にすることが出来る、1つの選択肢として良さそうなツールだと思いました。
mockioを使ってみた
タイトルどおり、mockioを使ってみただけの記事です。
最近、偶然Xで以下のツイートを見てmockioの存在を知りました。
mockgenから先日知った動的にモックできるパッケージに移行してみているけど、移行コストが低くてサクッとできてしまった
— ゴリラ - 何気ない日々に感謝を (@gorilla0513) 2025年4月24日
めっちゃ便利https://t.co/3VwQlJKB4p
公式
公式ドキュメントとgithubは以下のとおり。
Javaのmockitoにインスパイアされたモックライブラリ、とのこと。 site.mockito.org
お試し
お試し対象のコード
まずはちょっとしたテスト対象となるコードを作成します。 すごくシンプルな処理で単純にユーザー名を返却するだけの処理になります。
ちょっとだけ階層化していて、usecase -> repositoryのような呼び出し順序にしています。
main.go
package main import ( "fmt" "test1/repository" "test1/usecase" ) func main() { userRepo := repository.NewUserRepository() usecase := usecase.NewGetUserUsecase(userRepo) username := usecase.Execute() fmt.Println(username) // UserName }
usecase/user.go
package repository type UserRepository interface { GetUser() string } type userRepository struct{} func NewUserRepository() UserRepository { return &userRepository{} } func (r *userRepository) GetUser() string { return "UserName" }
repository/user.go
package repository type UserRepository interface { GetUser() string } type userRepository struct{} func NewUserRepository() UserRepository { return &userRepository{} } func (r *userRepository) GetUser() string { return "UserName" }
ユニットテスト
usecaseに対してユニットテストを記述し、この中で今回のmockioを試してみようと思います。
usecase/user_test.go
package usecase_test import ( "test1/repository" "test1/usecase" "testing" "github.com/ovechkin-dm/mockio/v2/mock" ) func TestGetUserUsecase_Execute(t *testing.T) { ctrl := mock.NewMockController(t) m := mock.Mock[repository.UserRepository](ctrl) mock.WhenSingle(m.GetUser()).ThenReturn("TestUserName") type args struct { userRepo repository.UserRepository } tests := []struct { name string args args want string }{ { name: "test", args: args{ userRepo: m, }, want: "TestUserName", }, } for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { u := usecase.NewGetUserUsecase(tt.args.userRepo) if got := u.Execute(); got != tt.want { t.Errorf("GetUserUsecase.Execute() = %v, want %v", got, tt.want) } }) } }
ポイントはテストコードの最初でモックを作成している箇所だと思います。 * NewMockControllerでmock用のコントローラを作成 * モック対象のrepository.UserRepositoryのモックを作成します。 * モックしたい関数を指定、さらに呼び出された時の返却を指定します。
感想
gomockなどに慣れていれば、すぐに理解できると思います。 内部実装をちょっとだけ見てみたのですが、gomockとは違い、genericsをうまく活用してコード生成せずともモックできるようにしてているようでした。
本当に少し触っただけではありますが、使い心地も良さそうなので、もう少し調査した上で仕事にも活用していこうと思いました。
Goのユニットテスト時に非公開フィールドのオブジェクト同士を比較する
ユニットテストを記述する際、go-cmpを使用することは多いと思います。
ただ、なぜかgo-cmpでは非公開フィールドは比較されないものと思い込んでいました。 これはデフォルトの動作ではpanicが発生していてからです。 しかし、実際には非公開フィールドについてもちゃんと比較可能でしたので、具体的なやり方を残しておきます。
非公開フィールドを比較する
AllowUnexportedオプションを指定することで比較可能になります。 以下、サンプルです。
type User struct { name string age int prefecture string } // 比較サンプル func sample1() { // 比較オブジェクトが同じ場合 user1 := User{name: "user1", age: 30, prefecture: "Tokyo"} user2 := User{name: "user1", age: 30, prefecture: "Tokyo"} result1 := cmp.Diff(user1, user2, cmp.AllowUnexported(User{})) fmt.Println(result1) // Should print an empty string // 比較オブジェクトが違う場合 user3 := User{name: "user1", age: 30, prefecture: "Tokyo"} user4 := User{name: "user2", age: 32, prefecture: "Tokyo"} result2 := cmp.Diff(user3, user4, cmp.AllowUnexported(User{})) fmt.Println(result2) // Should print: // main.User{ // - name: "user1", // + name: "user2", // - age: 30, // + age: 32, // prefecture: "Tokyo", // } // } }
オブジェクトが違う場合、違う箇所も見やすく表示してくれて良い感じです。
比較しない項目を指定可能かどうかの検証
ユニットテストでは一部項目を比較したくないケースもあるかと思います。 その場合、go-cmpではIgnoreFieldsを指定することで可能なのですが、これが非公開フィールドでも同様かどうか検証しました。
結果としては非公開フィールドでもignoreFieldsオプションは使用可能でした。 以下、サンプルです。
type User struct { name string age int prefecture string } // ageとprefectureを無視して比較 func sample2() { user1 := User{name: "user1", age: 30, prefecture: "Tokyo"} user2 := User{name: "user1", age: 32, prefecture: "Chiba"} result1 := cmp.Diff(user1, user2, cmp.AllowUnexported(User{}), cmpopts.IgnoreFields(User{}, "age", "prefecture")) fmt.Println(result1) // Should print an empty string }
ユニットテスト内で使用する
特に工夫なく、そのまま使用します。 testifyパッケージのassert.Emptyと一緒に使用すると良さそうに思います。
assert.Empty(t, cmp.Diff(expected, got, cmp.AllowUnexported(item.Item{}), cmpopts.IgnoreFields(item.Item{}, "itemType")))
cmp.Equalという関数もあり、比較オブジェクトが同じ場合にはtrue、違う場合にはfalseを返却するのですが、違う場合でも何が違うのかがわからない為、ここではcmp.Diffを使用しています。
最後に
このライブラリに限らずですが、何かを使用するのであれば、公式ドキュメントをちゃんとみないと上記のような思い違いをしてしまうことを痛感しました。