Try Golang! ファイル出力のテストを書こう
How to write test code for file output in Golang
ファイル出力のテストは、テスト実行時に実際にファイルが出力されるため、実行環境や出力されたファイルの削除などが面倒で、これまでテストを書いていませんでした。ただ、つい先日見つけた「golang で終了を確認するテストの書き方」という記事を参考にした結果、ファイル出力のテストもGoの世界だけで完結できたので、今日はその紹介をします。もっと良い方法があれば教えていただけると嬉しいです。
テスト対象のコード
テストの対象は、Output
関数です。引数のmulti
フラグがtrue
の場合は複数に分けて、false
の場合はまとめてファイルを出力します。
テスト対象コードのリファクタリング
まずはテスト対象のコードをリファクタリングしましょう。os.Create
関数をそのまま使うとファイルを出力してしまうので、テスト時はここを切り替えられるように関数リファレンスに変更します。下記のように変数createFile
を定義して、os.Create
を直接呼び出している箇所を置き換えます。
単純に var createFile = os.Create
としなかったのは、戻り値が*os.File
だとテストが書きにくいので、必要最小限のメソッドであるWrite
メソッドとClose
メソッドを持っているインタフェースにしたかったからです。
テスト対象コードのリファクタリングはこれだけです。次にテストコードを書いていきましょう。
テストコード
エラーハンドリングなど細かな部分は省略しつつ、テストコードはこんな感じ。Output
関数の引数strs
には[]string{"foo", "bar"}
を指定し、引数multi
にtrue
を指定するケースと、false
を指定するケースの2つをテストしています。ちょっと長くなりましたが、ご容赦ください。
ポイントは、リファクタリング時に追加したcreateFile
(シグニチャは func(name string) (io.WriteCloser, error)
)に設定可能なテスト用の関数を用意している点です(20~24行目)。この関数では、Writerとして*bytes.Buffer
使用しています。ただし*bytes.Buffer
はCloserインタフェースを満たさないので、11行目のnopCloser
を使って、何もしないClose
メソッドを追加しています。
また、Output
関数が出力した結果を確認する必要があるため、ファイル名をキーとしたマップを用意し(19行目)、bytes.Buffer
のポインタを設定してあげます(22行目)。これで、Output
関数実行後に、出力結果をマップから参照することができます。
ちなみに、25行目でSetCreateFile
という関数が登場しています。これは、テストコードのパッケージがsample_test
であり、テスト対象コードのパッケージsample
内のPrivateな変数であるcreateFile
を直接参照できないため、export_test.go
にて、下記のようにテスト時のみ外部パッケージに公開しているからです。
今回のテストだけを実施するのであれば不要なのですが、テスト用に一時的に値を置き換える場合は、テスト終了時に値をリセットできるようにするのが望ましいです。そのため、戻り値としてテスト用の変更をリセットする関数を返し、defer
で呼び出すだけでよいようにしています。この辺りはこちらの記事がとても参考になります。
どこまでテストを書くかは状況によりけりですが、時間が許すのであればできるだけ書いてあげた方がよいと私は思っています。自分ではない誰かや、書いたコードをすっかり忘れてしまった未来の自分がコードを修正する際に、きっと役に立つはずです!