はじめに

みなさん、テストは書いてますか?

気合を入れて「よしテスト駆動開発だ!」と思ったところで、実際テストを書きながら実装するのは慣れてないと思った以上に大変です。分岐が多くてテストの数が増えたり、どうしてもテストでたどりつきそうにない場所ができてしまったり、テストが遅すぎたり外部に依存して結果が毎回変わったり・・・

テストを書くためには、それ相応の「テストしやすい実装」が必要です。今回は新しいフレームワークを使う中で気がついた「テストのしやすい実装=テスタビリティの高いコード」の特徴を紹介します。

テストのしやすいコードとそうじゃないコードの違い

テストを書きやすいコードとテストを書きづらいコードの違いはなんでしょうか?

ぱっと思いつくものをあげてみましょう。

  • 分岐が多い
  • 他のクラス・外部のサービスに依存している
  • ランダムで結果を変える必要がある

こうしようと思ってこうするものはひとつもないと思いますが、ほっておくとついついこういう実装になってしまいがちです。最初はシンプルだったはずなのに、機能追加や仕様変更で気が付いたらスパゲッティに・・・なんてことは決して珍しくありません。

じゃあどうすればテストがしやすいのか?これはつまり逆の特徴を持っていれば良いわけです。

  • 分岐が少ない
  • なるべく他に依存しない
  • 毎度同じ結果をちゃんと返す

前半は意識すればどうにかなるかもしれませんが、後半はどうでしょう?どうしても外部のサービスが必要なものや、絶対ランダムにしないきゃいけない処理ありますよね?そういうのはどうしたらいいんでしょうか? 答えのひとつが「そういうものだけを切り出して別にテストする」です。

1つのメソッドでやることをなるべくシンプルにする

例えばここに UserId を元にしてユーザーが存在すればそのユーザーの情報(お気に入りと友達)を取得する処理があるとします。最初はシンプルなのでひとつのメソッドに詰め込んでしまっても良いでしょう。

このパターンならテストも簡単です。ユーザーが存在していれば UserInfo が、そうでなければ None が正しく返ってくるかをチェックするだけです。

ところがこれにさらに条件が加わった場合はどうでしょう?例えば、友達は相互に友達に設定している場合だけ出そう、とか。ついこういう修正をしてしまいがちですよね。

実装自体は大したことない変更なんですが、テストをするのは面倒になりました。いままではユーザーが存在する、しない、の2通りで済みました。今度はユーザーが存在した場合にさらに”友達がいない”、”友達いるけど相互じゃない”、”友達と相互に友達”をチェックしなければいけません。(もしかしたら”こっちは友達じゃないけど誰かに片想いされてる”もチェックする必要があるかもしれません。)合わせると4通りになります。

一見シンプルな実装に見えても、このようにひとつのメソッドの中に複数の処理が混在していればテストのパターンはねずみ算式に増えていくことになります。例えばお気に入りの中身が3通りくらいあってそれぞれに別の処理を追加して・・・となれば4*3=12通りのパターンを試さなくてはなりません。たった1つのメソッドでも12通りのテストを書かなければいけないとしたらどうでしょう?テストを全部ちゃんと書こう、とはちょっと考えたくなくなりますね。

一方この友達に関する処理は、自分自身の userId さえあれば問題なく処理できます。別に getUserInfo の中で処理する必要はありません。なので、この部分は思い切って外に出してみましょう。

こうすることで getUserInfo は最初と同じようにユーザーが存在するか否かの2通りだけをテストすれば良くなりました。そして、友達か相互かどうかは getMutualFollower のテストをかけばいいのです。

この例だとまだ getMutualFollower もテストが3パターン必要なので相変わらず4通りのテストを書く必要がありますが、先ほど例に出した”お気に入りが3パターンくらいの処理”になったとしても同じように切り出すことができます。そうすればそこもまたお気に入りだけで3パターンのテストを書けば良いため、1+3+3=7通り とテストを書くパターンを減らすことができました。

テストの数を減らすだけじゃないメリットもあります。 例えば、お気に入りに関する処理が変わった時に元の実装のままではお気に入りの1パターンだけを変えたとしてもそのパターンを使うすべてのパターンでテストを書き直さなければなりません。一方メソッドを分けていれば、修正してテストをやり直すのはその変更を加えた1パターン分だけで良いのです。

他に依存する部分とそうじゃない部分を切り離す

どうしても外部に依存してしまうような実装はどうしたらいいでしょうか?

例えばこんなのはどうでしょう?RSSリーダーで取得した情報を別のサービスに投げる処理をイメージしてください。

すごく面倒ですね。まず、RSSで何が入ってくるかがわからない。これでは実行するたびに結果が変わってしまうでしょう。それに、その後サイトによって処理を分岐したいのですがテストの時いつも全てのサイトが含まれているとも限りません。

そしてOtherServiceにデータを送信しようとしているのですが、これも困りものです。ちゃんと送信したかはどうやって確かめるのでしょうか?もしOtherServiceが停止していたら??

RSSReaderOtherServiceも「どんな動きをするかこちらではわからない・決められない」のが困りものです。じゃあ、これをこちら側でどんな動きをするかわかるものに取り替えましょう。

まず、RSSReaderはフィールドに一度もたせて、それを使うようにしました。これであればテストする際にはrssReaderを上書きしてモックと置き換えてやることで、テストの時も毎回望んだ動きをさせることができます。ただ、フィールドが増えてしまうことが問題になることもあります。

OtherServiceは引数で与える形式に変更してみました。実際に使う時には引数にわざわざ設定する手間がありますが、テストの時はモックを与えてやれば非常に楽にテストをすることができます。モックであれば何を送られたかをチェックすることも簡単にできるでしょう。

あえて二つを別々の方法で置き換えてみました。このように他に依存するものはなるべくテスト時に差し替えができるようにしておくことで、影響を気にすることなく毎回確実なテストが可能です。

この方法はランダムで結果が変わるようなテストにも有効で、ランダムな値を出す部分をこうやって切り出すことで、テストの時はランダムではなく毎回決まった値での処理でテストすることができます。

まとめ

テストを書くためには、一つ一つの処理をシンプルにして、細かく切り分ける工夫が有効です。また、こうすることで誰にとっても読みやすいコードにすることができるでしょう。テストがし易い実装というのはシンプルで洗練されたコードということにもつながると思います。

テストというのは最初にきちんと書き始めておかないと後から追加するのはとても辛いものです。テストファーストにこだわる必要はないと思いますが、きちんと書いておくにこしたことはないのです。まずは「テスト書くのが面倒だからいいや」ではなく、そう感じた時はなにか実装が長くて複雑になっているのかも?と疑問を持ってみましょう。。