読者です 読者をやめる 読者になる 読者になる

既存のmethodを壊さずに内部のmethodを差し替えて実行したい

はじめに

例えば、以下のような状況で M.F0() を呼びたい。しかしその内部で呼ばれる m.f()は呼んでほしくない場合。

type M struct {
}

func (m *M) F0() error {
    // do something
    return m.f()
}

func (m *M) F1() error {
    // do something
    return m.f()
}

func (m *M) f() error {
    // この処理を呼びたくない
    panic("don't call!!1")
}

通常のアプリケーションコードとして結合された状態で呼ばれる分には何ら問題無いコードも、テスト中では呼び出したくない場合がある。 例えば、 M.f() が外部のリソースに依存するような処理だった場合など、テスト中では呼び出したくない。

もちろん現状で以下の呼び出しはpanicする。

m.F0() // panic
m.F1() // panic

この時、公開された関数である M.F0() , M.F1() の内部で呼ばれる M.f() の挙動を差し替えたい。

単純なembedではダメ

以下はどう考えてもダメ。

type mockM struct {
    *M
}

func (m *M) f() error {
    // こちらが呼ばれてくれると嬉しいけれど
}

embedにより書き換えたつもりになっているのかもしれないけれど。mockM.F0() の呼び出しで、実質呼ばれるのは内部の M.F0()。 その為、結局 M.f() の方が呼ばれてしまう。

内部の部分を切り分けてembedするのが無難そう

内部の部分を切り分けてembedするのが無難そう。型定義を以下の様に変える。

type mClient interface {
    f() error
}

type M struct {
    mClient
}

内部で利用するclientのinterfaceを定義。 デフォルトではactualMClientの方を利用し、テストの時などはmockedMClientの方を利用する。

type actualMClient struct {
}

func (c *actualMailerClient) f() error {
    // この処理を呼びたくない
}

type mockedMClient struct {
}

func (c *mockedMClient) f() error {
    // こちらが呼ばれてくれると嬉しい
}

以下のような関数があるとうれしいかも?

func NewM() *M{
    client := actualMClient{}
    m := M{mClient: &client}
    return &m
}