テスト時のinterfaceに対するfake objectの作成にinterfaceの埋め込みを使う

過去の以下の記事の続き。

この方法はたまに使われていたりする。ただリンク先の記事ではinterfaceを実装したstruct(アプリで実際に利用しているstruct)を埋め込んでいる。

これはテスト中にはinterfaceを埋め込んだほうが良いかもしれないという話。

以下のようなインターフェイスがある。このインターフェイスF()G()の二つのメソッドを要求している。

type I interface {
    F()
    G()
}

先程のインターフェイスIのうち、F()だけに依存するコードのテストのfake objectは以下で十分。

type fake struct {
    I
}

func (f *fake) F() {
    fmt.Println("f")
}

実際以下の様なコードはコンパイルできる。

func use(i I) {
    i.F()
}

func main() {
    fake := &fake{}
    use(fake)
}

定義していなかったほうのメソッドが呼ばれた場合

先程のfake objectはF()の利用のためにF()だけを定義していた。実際にはG()の実装が要求される場合にはnil dereference panicになる。

--- 00/main.go   2018-09-21 22:14:55.873712187 +0900
+++ 01/main.go    2018-09-21 22:15:15.050405975 +0900
@@ -18,7 +18,7 @@
 }
 
 func use(i I) {
-  i.F()
+   i.G()
 }
 
 func main() {

初見では原因や修正箇所が分かりづらいかもしれない。

panic: runtime error: invalid memory address or nil pointer dereference
[signal SIGSEGV: segmentation violation code=0x1 addr=0x20 pc=0x48220e]

goroutine 1 [running]:
main.(*fake).G(0xc42000e1e0)
    <autogenerated>:1 +0x2e
main.use(0x4c39e0, 0xc42000e1e0)
    VENV/individual-sandbox/daily/20180920/example_embed/01/main.go:21 +0x31
main.main()
    VENV/individual-sandbox/daily/20180920/example_embed/01/main.go:26 +0x3d
exit status 2

gist