fresh digitable

めんどくさかったなってことを振り返ったり振り返らなかったりするための記録

重い腰を上げてテストのないクラスのテストを書くときまず最初にやること

テスト書くの後回しにしちゃったなーでもこの関数のテスト書かなきゃ…みたいなとき、まずどうするか。大事なことは最初のハードルを下げたり、ステップを小刻みにすること。

とりあえず最初はテスト対象のインスタンスを生成してそれがNonNullであるというテストを書く。つまりインスタンスを正しく作れるかというテストで、必要なことはコンストラクタに渡すインスタンスをどうやって作るかということだけである。だけなのだが、面倒なことがいくつかある。例えば、モックを作って渡すなら初期化時に呼ばれる関数の戻り値を設定しなければならない。そのほかにも、AndroidならContextなどプラットフォームのクラスを中で使っている場合はAndroidJUnit4をテストランナーとして設定する必要がある。うっかりLog.d()が入り込んでいたりするとTimberに置き換えるといったような別の作業も発生するかもしれない。

最初にやらなきゃいけないことが実はたくさんあるので、いきなり本題のテストを書き始めようとしてもなかなか進まずイライラすることになる。また、これらの作業を経ることによってimportがいい感じに温まっているという副産物もある*1

最初のテストが書けたら、次は書こうと思っていた関数のテストのうち、もっとも単純なケースのテストをとりあえず2・3個書いてみる。TDDでレッドとグリーンの間を行ったり来たりしてもよい。きれいなコードを書く必要はなく、コピペしてテストの関数名と渡すパラメータを少し変えたようなものでよい。いくつか書いていくと、自分が本当はどんなことをこのテストで確認したいと思っているのかということや、テストに共通で登場する前提条件や処理群といったものが明らかになる。特に共通する処理の最たるものというのはほかでもなく、最初に書いたNonNullテストの中の初期化処理である。これをひとまずはsetupのための関数として使えるように、TestアノテーションをBeforeアノテーションに置き換え、生成したインスタンスを各テストケースからアクセスできるようプロパティにセットする*2

あとはコピペで作ったテストから重複するコードを取り除いたり、いっそのことパラメタライズドテストに書き換えてみたり、テストクラスが大きくなったら前提条件ごとに内部クラスに分割したり、どの内部クラスでも使うRuleをまとめて使えるようにしたRuleを作ったりしながらテストを育てていけばよい。

まとめ

こんなブログを読むよりもレガシーコード改善ガイドとかを買って読んだ方が明らかに役に立つ。

*1:テンプレートを作っておけという話もある。

*2:Java的なアプローチですね。Kotlinならlazy {}の中に押し込んでもいいのかもしれない。テストの前提条件がいい感じにまとめられるとよい。