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