devlog

Go で標準出力をテストするために DI した

2023-08-28

Go言語でCLIを作っているとき標準出力をテストしたくなりました。
os.Stdout を書き換える手法もあるようですが、面倒だったのでシンプルにDIしました。

interface の定義

interface を定義して依存性の注入 (DI) をします。
ここでは Printf というメソッドを用意しました。引数は fmt.Printf と同じです。

type RendererInterface interface { Printf(format string, a ...any) }

 
この interface を実装します。DIするので struct は2つ必要です

  • Renderer ... アプリケーションを実行する時に使う。通常時
  • MockRenderer ... テストの時に使う
package main import ( "fmt" ) // interface type RendererInterface interface { Printf(format string, a ...any) } // アプリケーションを実行する時に使う。通常時 type Renderer struct {} func NewRenderer() *Renderer { return &Renderer{} } func (ren *Renderer) Printf(format string, a ...any) { fmt.Printf(format, a...) } // テストの時に使う type MockRenderer struct { Out string } func NewMockRenderer() *MockRenderer { return &MockRenderer{} } func (ren *MockRenderer) Printf(format string, a ...any) { ren.Out += fmt.Sprintf(format, a...) }

 

テストする

さて、上のコードを用いて標準出力をテストしてみます。
 
テスト対象のコードです。Hello という関数をテストします

func Hello(renderer RendererInterface) { renderer.Printf("hello\n") } func main() { renderer := NewRenderer() Hello(renderer) }

 
テストコードです。Renderer の代わりに MockRenderer を入れます。

package main import ( "testing" "github.com/stretchr/testify/assert" ) func TestHello(t *testing.T) { renderer := NewMockRenderer() Hello(renderer) assert.Equal(t, renderer.Out, "hello\n") }

感想

他にも方法はありますが、ここではシンプルにDIしてみました。標準出力も副作用であると捉えてます。 なお、この方法を用いると fmt.Printf を普通に呼び出すことは出来なくなります。当たり前ですが。

少し面倒ですね。。

  • 作成日
    2023-08-28
  • 更新日
    2023-08-28