概要
「GoはDuck TypeだからMock用意するの大変だよね」とよく言われますが、そんなことはないですよ、という話。
環境
- golang 1.8.3
1. interface自体埋め込めば実装済みと解釈してくれる
例えば以下のような複数のメソッドを持つinterfacedoEverything
があり、showUserAge()
にDIして表示させるロジックがあるとします。
※DIについては GoでDependency Injection - Carpe Diem を参考にしてください
type doEverything interface { getAge() int setAge(int) error getName() string setName(string) error getSex() string setSex(string) error } func showUserAge(d doEverything) { fmt.Println(d.getAge()) }
Mock化する場合、Duck Typeなので全メソッドを実装すれば引数に入れることができますが、それを1つ1つ書くのは非常に手間です。
そんな時は使うメソッドだけ実装し、それ以外はinterface自体を埋め込めば実装済みと解釈してくれます。
func main() { m := &Mock{} showUserAge(m) } type Mock struct { doEverything } func (m *Mock) getAge() int { return 10 }
※当然ですが、実装してないものを使おうとするとpanicになるのでモックとしての使い方とだけ考えてください
2. なるべく小さいinterfaceにする
そもそも1つのinterfaceに色々なメソッドを用意するから大変なのであって、Go Proverbs で
The bigger the interface, the weaker the abstraction
とあるように、メソッドが多すぎるのは設計自体がよろしくない、と言えます。
複数の責務を持つことになって凝集度は下がりますし、具体性が増す分、抽象度が下がります。
なので標準パッケージのようになるべくinterfaceが持つメソッドは少なくすると良いです。
悪い例
type doesEverything interface { countChickens() int hatch() checkWeather(lat, lng float64) string checkScores(home, away team) (int, int) checkYourself() bool wreckYourself() }
良い例
type Reader interface { Read(p []byte) (n int, err error) } type Writer interface { Write(p []byte) (n int, err error) }
まとめ
Goでモック化するのに疲弊している方にとって参考になれば幸いです。