Try Golang! ファイル出力のテストを書こう

How to write test code for file output in Golang

Takuo
VELTRA Engineering
4 min readApr 22, 2019

--

ファイル出力のテストは、テスト実行時に実際にファイルが出力されるため、実行環境や出力されたファイルの削除などが面倒で、これまでテストを書いていませんでした。ただ、つい先日見つけた「golang で終了を確認するテストの書き方」という記事を参考にした結果、ファイル出力のテストもGoの世界だけで完結できたので、今日はその紹介をします。もっと良い方法があれば教えていただけると嬉しいです。

テスト対象のコード

テストの対象は、Output関数です。引数のmultiフラグがtrueの場合は複数に分けて、falseの場合はまとめてファイルを出力します。

テスト対象コードのリファクタリング

まずはテスト対象のコードをリファクタリングしましょう。os.Create関数をそのまま使うとファイルを出力してしまうので、テスト時はここを切り替えられるように関数リファレンスに変更します。下記のように変数createFileを定義して、os.Create を直接呼び出している箇所を置き換えます。

単純に var createFile = os.Create としなかったのは、戻り値が*os.Fileだとテストが書きにくいので、必要最小限のメソッドであるWriteメソッドとCloseメソッドを持っているインタフェースにしたかったからです。

テスト対象コードのリファクタリングはこれだけです。次にテストコードを書いていきましょう。

テストコード

エラーハンドリングなど細かな部分は省略しつつ、テストコードはこんな感じ。Output関数の引数strsには[]string{"foo", "bar"}を指定し、引数multitrueを指定するケースと、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で呼び出すだけでよいようにしています。この辺りはこちらの記事がとても参考になります。

どこまでテストを書くかは状況によりけりですが、時間が許すのであればできるだけ書いてあげた方がよいと私は思っています。自分ではない誰かや、書いたコードをすっかり忘れてしまった未来の自分がコードを修正する際に、きっと役に立つはずです!

--

--

Takuo
VELTRA Engineering

Engineer who likes travel, simple code, and something new