自動テストを導入することにより、テストケースの作り方を統一でき、網羅できます。全体を自動テストにできれば、変更部分以外の障害を防止できます。そして、テスト作業がコーディング作業になることによって、楽しくなるでしょう。実際のプロジェクトに導入するにあたってはいくつかの課題がありますが、自動テスト用のテストデータをあらかじめ用意しておくこと、DbUnit・Mockito・djUnitを使うことで解決できます。
対象読者
今回の対象読者は、下記のとおりです。
- 実際の開発プロジェクトへの自動テストの導入を検討されている方
- JavaによるWebアプリケーション開発についての知識がある方
- JUnitの基本的な知識がある方
必要な環境
- JDK 7
- Eclipse 4.3
- Tomcat 7
自動テスト導入における課題
JUnitの使い方は簡単なので、試しに使ってみたという方は多いと思います。しかし実際に業務で使うとなると、課題が多く容易ではありません。
最初の課題として挙げられるのは、個人作業とチーム作業の違いです。チームで作業する場合は、ルール作りが必要です。一人一人が自分勝手な方法でテストコードを書いたとしても、その場限りのテストになってしまいます。そのプログラムを他の人が改変したときに、テストコードも変えてテストが通るようにしなければなりません。また、そういったルールを作る必要もあります。
次に、大規模なデータベースアプリケーションでは、テストデータをどうするかという問題があります。DBアクセスはせずモックを使うという方法もあるでしょう。しかし、できればSQLもテストしたいところです。テストデータは都度作ってテスト実行後に消すというのが基本です。しかし大規模なアプリケーションだと、稼働させるために多くのマスタテーブルを用意し、フィールドがたくさんあるテーブルのデータの用意が必要です。毎回すべて用意するのは非現実的ですし、プログラムのINSERT文でデータを投入するのも大変です。
また、もともと自動テストを前提に設計したアプリケーションではないものに対し、自動テストを適用するのも、一筋縄ではいかないものです。例えばHttpServletRequestやHttpSessionは、JUnitで値を投入できないので、テストも簡単ではありません。オイシックスのECサイトのシステムは、JUnit登場前に作ったものがベースになっているので、そのままではほとんど適用できるところがありませんでした。自動テストを前提に設計していれば、例えばStruts 2ならHttp関連のクラスはMapで外から投入できるようになっています。また、Springを使えばモックに入れ替えたいオブジェクトはDIしておくように作っておいて、単体テスト時には入れ替えて動かすといったこともできます。しかし、もともとそのように作っていない場合は、ちょっと直す程度では対応できないので、全面的に作り直しになってしまいます。
自動テスト導入の目的
創業からしばらくの間、オイシックスでは品質以上にいかに早く機能をリリースするかが重要でした。しかし次第にお客様が増えるにつれ、一つのバグが多くの方にご迷惑をおかけするようになってきました。そうなるとクレーム対応に追われ、会社を成長させる活動が難しくなります。そのため、より高い品質が必要になったのです。
その後は手動でのテストの改善を行ってきましたが、いくつか課題がありました。
バグが発生する原因の一つにテストケース不足があります。オイシックスの開発者はほとんどが中途採用です。人によってテストケースの洗い出し方、テストケースの書き方、確認の仕方など思った以上にばらばらです。よくあるのはコードにifがあるのにelseの場合をテストしていなかったり、条件の組み合わせを網羅していなかったり、テストケースがあいまいだったりといったことです。つまり、○という操作をしたら○という結果になる、という部分が具体的ではなかったのです。それを統一するためマニュアルを作成しました。
マニュアルどおりにテストケースを洗い出し、もれがないかレビューをする必要があります。レビューはするのですが、本当に網羅しているかどうかのチェックは、なかなか難しいものがあります。ソースコードを全部見て理解したうえで見ていかないといけないので、コーディングやテストを実際にするのに近い工数がかかってしまいます。そのため実際にはざっと見て気がつくところしか指摘できず、レビューによっての網羅はなかなか担保できません。
もう一つ、オイシックスではオブジェクト指向開発をしており、ロジックが重複しないようできるだけ共通化しています。そうすると、ある部分の修正が別の思わぬ部分に影響してトラブルになるといったことが起こるようになりました。変更部分が他にどういうところから呼ばれているか確認してテストする必要がありますが、全部をテストするのは実質的に難しい場合が多いです。
品質を重視するようになった結果、別の問題も出てきました。それは、コーディングよりもテストのほうに工数がかかってしまうことです。本来はそれが正しいのかもしれませんが、開発者としては、やはりコードを書きたいのです。アプリケーションの規模が大きくなればなるほど、少しコードを書いただけで、たくさんのテストをしなければなりません。
自動テストによる解決
では自動テストによって、どのように解決するかを考えていきましょう。
自動テストによってテストケースの記述が具体的に統一できます。文書だとあいまいなテストケースも書けてしまいますが、自動テストではそういうわけにはいきません。assertThat(is(xxx))といったように、具体的な形になるのは必然です。
ツールを使うことでカバレッジを確認できます。カバレッジというのはテストケースがどれくらい網羅されているかということです。これにより、開発者は自分でテストケースが足りているかどうかを確認できます。またレビューする際も、カバレッジレポートで100%になっているか確認するだけで済みます。
他の部分への影響という意味では、テストコードが全体を網羅できれば変更したときに全部のテストを通してみることで確認ができます。全テストを流してエラーが出たら、変更の仕方を変えるか、エラーが出た箇所を修正することで、トラブルを未然に防ぐことができます。
また自動テストにすることで、テスト作業がコーディング作業に変わります。テストは退屈な作業ですが、テストコードを書いてテストを通すのは楽しい作業です。筆者はテストのルールを設計するためにいくつかのクラスでテストコードを書いてみましたが、手作業によるテストと比べて実に楽しいものでした。毎日テストコードを書けと言われたら喜んでやります。
実際の業務への導入にあたって
実際の業務に導入するにあたっては、最初に挙げたような課題がありました。
これらをどのように解決したかというと、まずは共通ルールを作りました。
テストデータについての詳細は、のちの連載で解説しますが、ある程度のデータはあらかじめ用意しておくということと、DbUnitを使いました。DbUnitは優れたツールですが、JUnit4で使う場合は、ややイレギュラーなやり方をする必要がありました。
また自動テストを前提に設計していない部分について、サーブレット関連のクラスについてはMockitoというモックフレームワークを使いました。Mockito以外にもEasyMockやPowerMockなどのモックフレームワークがありますが、Mockitoのインターフェースはとても洗練されていて書くのも読むのも分かりやすく、気持ちがよいです。DBアクセス部分をモックに入れ替えたりするのは難しいのでそのままDBアクセスします。テストデータが課題になるのはそういう事情もあります。
カバレッジレポートを作成するにあたっては、djUnitを使いました。DbUnitに名前が似ていますがまったくの別物です。こちらもEclEmmaなど他にも同様のものがありますが、オイシックスのシステムや業務にはdjUnitのほうが適していたので、こちらを使うことにしました。なお、djUnitにはカバレッジレポート以外にもいろいろな機能が用意されていますが、今回の連載では使用しません。
まとめ
自動テストを導入することにより、テストケースの作り方を統一でき、網羅できます。また、全体を自動テストできれば変更部分以外の障害を防止できます。さらに、テスト作業がコーディング作業になるので、楽しくなるのではないでしょうか。
実際のプロジェクトに導入するにあたってはいくつかの課題がありますが、自動テスト用のテストデータをあらかじめ用意しておくこと、DbUnit・Mockito・djUnitを使うことで解決できます。
次回は、自動テストの対象になるサンプルアプリケーションをご紹介します。