アドベンドカレンダー、12/9です。PSTだとまだギリギリ日付変わってないからセーフセーフと思ってたらそれも過ぎてた><
はじめに
さて、こんな記事を見られてるからには皆さんユニットテストをされていますね?
ユニットテストの良いところは単機能をシンプルかつ高速にテストできる事です。つまり結合テストの範囲を減らすことができる。
これは素晴らしいメリットですよね?
でもちょっと待ってください。本当に適切な「ユニットテスト」になっていますか?
JavaEEなあたなArquillianとか使ってませんか? DBUnitとかでデータベースのテストしてませんか? あるいはheadless+cucumberでE2Eテストをしていませんか?
もちろんこれらは素晴らしい機能です。どんどん使っていくべき機能です。ですが、この手のテストは単純に重いです。こういったFunctional TestとUnit Testを混ぜて実行するにはいくつかの弊害があります。
個人的にはFunctional Testを含む遅いテストを「Slow Test」と呼びビジネスロジックのみを検証し高速に終わる「Quick Test」とは区別するようにしています。
Slow Testを区別しないことの弊害
前提としJavaのMavenをベースに話しますが事情は他の言語やビルドツールでも大きくは変わらないでしょう。
Slow Testの問題は文字通り遅いことです。DBの初期かなりエミュレータの起動なりシミュレータの起動というのは一般的に結構な時間を食います。
テスト数が少ない時は問題ないのですが多くなってくるとビルド時間を数倍増やす、それも數十分単位で、という状態になってきます。
このような状態で何が起こるかというと「ビルド時にテストが実行されないようにtestSkipをする」という最悪のバッドノウハウの蔓延です。
この状態になると「ビルド時にテストを実行する」というカルチャーに影響を与えてしまい、結果としてテストが壊れても気にしない空気が形成されます。ちょっとしたホラーですね。
これを防ぐためにSlow Testを通常ビルドのタイミングで実行するのは避け、CIとかPushタイミングとか「然るべきタイミングのみで実行されるSlow Test」と「常にビルド時に走るQucik Test」で分けておくと便利です。
MavenでのSlowTestとQuick Testの分け方
Mavenだと大きく分けて以下のいずれかの方法になるかと思います。
- integration-test phaseを使う
- slow-test profileを作る
恐らくIntegration-test phaseを使うのがもっともレールに乗った方法です。
JUnitのテストケースに自作した@IntegrationTest
みたいなアノテーションを付与して、pom.xmlに上記のアノテーションがある場合はintegration-test-phaseのみで実行するようにしたものです。
これは実行順番もtest -> integration testになりますし普段のビルドにも利用されないので最適ですね。
ただ、個人的にはちょっと設定が煩雑なのでメンテビリティが低いことからシンプルにslow-testというまんまの名前のプロファイルを作ってテストケースのディレクトリを丸ごと切り替える方法を最近は使っています。
これは自動てquick-testとslow-testをまとめて実行とかはできないとか欠点も多いのですが構成がシンプルというメリットがあります。
resoucesも分離できるので重くなりがちなslow-testのディレクトリをgitのサブモジュール等で管理することも可能です。
以下の感じでpom.xmlを書きます。
<properties>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
<maven.compiler.source>11</maven.compiler.source>
<maven.compiler.target>11</maven.compiler.target>
<my-test-source-directory>${project.basedir}/src/test/java</my-test-source-directory>
</properties>
<profiles>
<profile>
<id>default</id>
<activation>
<activeByDefault>true</activeByDefault>
</activation>
<properties>
<my-test-source-directory>${project.basedir}/src/test/java</my-test-source-directory>
</properties>
</profile>
<profile>
<id>slow_test</id>
<activation>
<activeByDefault>true</activeByDefault>
</activation>
<properties>
<my-test-source-directory>${project.basedir}/src/test/slowtest</my-test-source-directory>
</properties>
</profile>
</profiles>
<build>
<testSourceDirectory>${my-test-source-directory}</testSourceDirectory>
</build>
ポイントはtestSourceDirectoryを直接profile切り替えで制御することは出来ないので、profileではプロパティの切り替えに留め、testSourceDirectoryはプロパティを参照するように実装しておくことです。
これによってプロファイルの切り替えでテストディレクトリの切り替えも実現ができます。
まとめ
quick-testとslow-testという用語は私の俺俺定義ですが課題としては一般的だと思うので、どっかでちゃんとした名付けがされてるかもしれないです。ご存知だったら教えてください。
[JavaでのUT作成基準を整理してみた] に今回記載したのと同じようなテストを実施すると時の観点そのものをまとめているので、良ければそちらもご参考を。
それではHappy Hacking!