このエントリーは、TDD Advent Calendar 2013の参加エントリーです。
前日のエントリーは、id:taczge さんのアサートファーストでテスト駆動開発 - Level.TRACEでした。
今回のエントリーは、2011年9月にザ・インタビューズに投稿したものの再掲載です。当時とは状況や考えが変わってきている部分もあるのですが、ザ・インタビューズが閉鎖するということもあり、史料的な位置づけとしてポストします。
Developer Testtingに関する読書会を主宰していながら扱っているのはレガシーコードばっかで歯がゆい思いをしていたのですが、今の部署に異動してからは周囲 の理解もあり、TDDで開発を進められています。あと今の所属がコストセンターなので、現場のプロジェクトでUnit Testを導入する際のCIの導入支援なんかもやっています。
直近のプロジェクトではレガシーコードな社内フレームワークに対してテストを整備しながら機能を追加する、ということをやったのですが、その時に苦労したこととなりますと、
(1)日本語テストメソッド名
自分はテストメソッド名は日本語以外ありえない!という派なのですが、メリットを理解してもらえずに苦労しました。最後はちょっと強引に押し切りました...
(2)Singleton
DIコンテナを導入してないのでどうしてもSingletonを使わざるをえず、テストを書く上で障害になりました。結局、Singletonなのに Factoryに「replace」とか「reset」とかいうメソッドが出来ることになり、自分でも苦しいと思っています。
(3)テストデータ(Fixture)の管理
Seasar(S2Unit)やDBUnit等ですとExcelやXMLでテストデータを管理します。自分は以前は(アーキテクチャーがSeasarでない場合も)S2UnitのためだけにSeasarを使っていたのですが、
・S2Unitの場合、テストケースごとに全てのテストデータを投入しなおすテスト戦略が前提なので、スローテストになりやすい。
・またテストデータのExcelのメンテナンス性が低い。
ということで、テストデータはSQL文で管理する戦略をとりました。具体的にはEclipseプラグイン(DBViewer)でDBに直接データを入れた後、それをDBViewerの機能でinsert文に変換して、それをSVNで管理する、という方法を取っています。
また、開発者ごとにデータベースを用意する、Database Sandboxを用意できたので、テスト全体の実行前にテストデータを投入していったんコミットし、個別のテストケースが加えた変更はロールバックする、 という方法をとっています。xUnit Test Patterns(xutp)でいうShared FixtureとTransaction Rollback Teardown の併用です。
(4)DBへのアサーション
DBのデータに対するアサーションが難しいのはみなさんご承知の通りです。S2Unitでは期待値をExcelで書きますが、Excelであるが故のメン テナンス性の低さと、CRUDの内のC・U・Dはテーブル全体に対する期待値を書かなければいかない、という問題があります。後、主キーを自動生成してい るときに期待値のテストデータが書けない、という問題があり、これは回避しようがありません。
そこでCUDに対しては、
・まずテーブル全体をダンプする。
・テスト対象を実行(SUTとエクササイズ)する。
・実行後のテーブル全体をダンプしてテスト前と比較
・比較結果をアサート
という戦略を取りました。xutpで言うDelta Assersionです。これによりテストケースの可読性とメンテナンス性をかなり上げることができました。
※SUT=System Under Testの略。テストケースに対するテスト対象。
(5)スキーマ管理
Database Sandboxの採用やRDBMSを複数サポートしていることもあり、管理するDBのインスタンスが20を越えているため、スキーマ管理はCIツールで行 うようにしています。DDLを修正してコミットした後CIを回すと、スキーマを自動で更新するようになっています。DROP IF EXISTがないRDBMSでも同様のことができるようにしているのと、PostgreSQLやMSSQLServerのようなDDLがトランザクション をかけるRDBMSにも対応しているのがみそです。
(6)Mock(mockito)の採用
もともと自分はMockオブジェクトを極力使わない派だったのですが、今回はServlet APIを使っている部分に対してテストを書く必要があったので、mockitoを使用しました。mockに慣れていないので多少の苦労はありましたが、テ ストの網羅率が格段に上がったのと、テスト対象クラスの実装ではなく「振る舞い」(Behavior)寄りのテストを書くことが出来るようになったという 効果がありました。
このプロジェクトでは最終的にテストケースを約400書き、C1カバレッジが80%程度でした(元はレガシーコードですよ!) 品質もいいものが作れたので(それ故の苦労があったりしましたが、それはここに書くにはちょっと...)。テストにかけたコストに対する見返りは十分に あったと思います。
直近のプロジェクトではレガシーコードな社内フレームワークに対してテストを整備しながら機能を追加する、ということをやったのですが、その時に苦労したこととなりますと、
(1)日本語テストメソッド名
自分はテストメソッド名は日本語以外ありえない!という派なのですが、メリットを理解してもらえずに苦労しました。最後はちょっと強引に押し切りました...
(2)Singleton
DIコンテナを導入してないのでどうしてもSingletonを使わざるをえず、テストを書く上で障害になりました。結局、Singletonなのに Factoryに「replace」とか「reset」とかいうメソッドが出来ることになり、自分でも苦しいと思っています。
(3)テストデータ(Fixture)の管理
Seasar(S2Unit)やDBUnit等ですとExcelやXMLでテストデータを管理します。自分は以前は(アーキテクチャーがSeasarでない場合も)S2UnitのためだけにSeasarを使っていたのですが、
・S2Unitの場合、テストケースごとに全てのテストデータを投入しなおすテスト戦略が前提なので、スローテストになりやすい。
・またテストデータのExcelのメンテナンス性が低い。
ということで、テストデータはSQL文で管理する戦略をとりました。具体的にはEclipseプラグイン(DBViewer)でDBに直接データを入れた後、それをDBViewerの機能でinsert文に変換して、それをSVNで管理する、という方法を取っています。
また、開発者ごとにデータベースを用意する、Database Sandboxを用意できたので、テスト全体の実行前にテストデータを投入していったんコミットし、個別のテストケースが加えた変更はロールバックする、 という方法をとっています。xUnit Test Patterns(xutp)でいうShared FixtureとTransaction Rollback Teardown の併用です。
(4)DBへのアサーション
DBのデータに対するアサーションが難しいのはみなさんご承知の通りです。S2Unitでは期待値をExcelで書きますが、Excelであるが故のメン テナンス性の低さと、CRUDの内のC・U・Dはテーブル全体に対する期待値を書かなければいかない、という問題があります。後、主キーを自動生成してい るときに期待値のテストデータが書けない、という問題があり、これは回避しようがありません。
そこでCUDに対しては、
・まずテーブル全体をダンプする。
・テスト対象を実行(SUTとエクササイズ)する。
・実行後のテーブル全体をダンプしてテスト前と比較
・比較結果をアサート
という戦略を取りました。xutpで言うDelta Assersionです。これによりテストケースの可読性とメンテナンス性をかなり上げることができました。
※SUT=System Under Testの略。テストケースに対するテスト対象。
(5)スキーマ管理
Database Sandboxの採用やRDBMSを複数サポートしていることもあり、管理するDBのインスタンスが20を越えているため、スキーマ管理はCIツールで行 うようにしています。DDLを修正してコミットした後CIを回すと、スキーマを自動で更新するようになっています。DROP IF EXISTがないRDBMSでも同様のことができるようにしているのと、PostgreSQLやMSSQLServerのようなDDLがトランザクション をかけるRDBMSにも対応しているのがみそです。
(6)Mock(mockito)の採用
もともと自分はMockオブジェクトを極力使わない派だったのですが、今回はServlet APIを使っている部分に対してテストを書く必要があったので、mockitoを使用しました。mockに慣れていないので多少の苦労はありましたが、テ ストの網羅率が格段に上がったのと、テスト対象クラスの実装ではなく「振る舞い」(Behavior)寄りのテストを書くことが出来るようになったという 効果がありました。
このプロジェクトでは最終的にテストケースを約400書き、C1カバレッジが80%程度でした(元はレガシーコードですよ!) 品質もいいものが作れたので(それ故の苦労があったりしましたが、それはここに書くにはちょっと...)。テストにかけたコストに対する見返りは十分に あったと思います。