Joinしたプロジェクトでユニットテストをしていないと分かったときの絶望感。
ということで、Java + Spring Frameworkという構成のプロジェクトにJMockitでユニットテストとカバレッジレポートを導入します。
構成
- Java 8
- Spring Framework 4.1系
- JUnit 4系
- JMockit 1.21
- Eclipse
STSを利用している場合EclipseをSTSと読み替えてください。
ほぼ同じです。
準備
pom.xml
プロジェクトの依存関係にテスト関係のライブラリを追加します。
pom.xmlに以下を追加してください。
1<dependencies>
2 :
3 <dependency>
4 <groupId>org.springframework</groupId>
5 <artifactId>spring-test</artifactId>
6 <version>${spring-ver}</version>
7 </dependency>
8 <dependency>
9 <groupId>org.jmockit</groupId>
10 <artifactId>jmockit</artifactId>
11 <version>1.21</version>
12 </dependency>
13 <dependency>
14 <groupId>org.jmockit</groupId>
15 <artifactId>jmockit-coverage</artifactId>
16 <version>1.21</version>
17 </dependency>
18 <dependency>
19 <groupId>junit</groupId>
20 <artifactId>junit</artifactId>
21 <version>${junit-ver}</version>
22 <scope>test</scope>
23 </dependency>
24 :
25<dependencies>
メモ
JMockitはグループIDが変わっています。
- 1.8以前:
com.googlecode.jmockit
- 1.8から:
org.jmockit
STSの設定
- 1.pom.xmlを編集後にプロジェクトを更新する
- 2.プロジェクトの設定を開き、[ ビルドパス > ライブラリ ]で
外部 Jar 追加(Add External JARs)
をクリックし、手元のMavenローカル・リポジトリからjmockit.jar
を選択する。 - 3.ビルドの順番(Order and Export)で、
JUnit
より先にjmockitが読み込まれるようにする。

メモ
eclipseのメニュー名称は環境によって若干異なるようですね。
また、Mavenのローカル・リポジトリの場所が分からないという場合はEclipseの設定を開き、[ Maven > ユーザ設定 ]をチェックしてみてください。
通常はホーム直下に.m2
があると思います。
私の場合は次の場所でした。(win)
1C:\Users\atuweb\.m2\repository\org\jmockit\jmockit\1.21
また、ビルドの順番にJUnit
が見当たらない場合は、JMockitをMaven Dependencies
の上にすればOKです。
テストを実装する
ディレクトリとパッケージ
- テスト用のディレクトリを
src/test/java
に追加し、この下にパッケージを追加していく - パッケージの構成はテスト対象のクラスと合わせる
例
net.atuweb.game.web.service.UserService.javaをテストする場合
net.atuweb.game.web.service.UserServiceTest.javaをクラスを作成する
テストクラスの書き方
1// JMockitを動かすためのアノテーション
2@RunWith(JMockit.class)
3public class UserServiceTest {
4
5 // テスト対象のサービスクラスをnewする
6 private UserService targetService = new UserServiceImpl();
7
8 // テスト対象クラスで@Autowiredしているものは@Mockedを宣言する
9 @Mocked
10 private CacheService cacheService;
11
12 // テストではDIされないため、モックしたものをそれぞれDeencapsulation.setField()する
13 @Before
14 public void setUp() throws Exception {
15 Deencapsulation.setField(targetService, cacheService);
16 }
17
18 // @Testアノテーションをつけたものがテスト対象と見做される
19 @Test
20 public void getSkillsTest() {
21 MSkill skill1 = new MSkill();
22 skill1.setId(17);
23 :
24 MSkill skill2 = new MSkill();
25 skill2.setId(18);
26 :
27
28 // テスト対象メソッド内でDIされたインスタンスの処理結果をセットし、任意の状態を作り出す
29 // resultは開業せずに同じ行に書くほうが多いようだ
30 new NonStrictExpectations() {{
31 cacheService.findSkill(17); result = skill1;
32 cacheService.findSkill(anyInt); result = skill2;
33 :
34 }};
35
36 // テスト対象メソッドの実行と、結果の検証
37 List<UserSkill> skills = targetService.getSkills(888L);
38 assertEquals(skills.size(), 2);
39 assertEquals(skills.get(0).getId(), skill1.getId());
40 :
41 }
42
43 // プライベートなメソッドのテストではリフレクションを用いる
44 @Test
45 public void calcHpTest() throws Exception {
46 Method method = UserServiceImpl.class.getDeclaredMethod(
47 "calcHp",
48 Integer.class,
49 int.class,
50 int.class
51 );
52 method.setAccessible(true);
53
54 assertEquals((Integer)method.invoke(targetService, 1, 3, 2), new Integer(2));
55 :
56 }
57}
テストの実行方法
eclipseからテストを実行する
- 1.eclipseのパッケージエクスプローラーからプロジェクトのトップレベルを右クリック
- 2.[実行(Run As) -> Maven test ]を選択する
ターミナルからテストを実行する
- pom.xmlがある位置までcdする
mvn test
とコマンドを入力し、実行する
カバレッジをチェックする
通常、実行パス直下にcoverage-report
ディレクトリが生成されます。
カバレッジの緑色の部分がテストで実行されているコードです。
なるべく緑色を増やして網羅率を上げていきましょう。
任意のディレクトリに出力する場合は、coverage-outputDir
オプションを指定してください。
オプションの設定方法については以下をご覧ください。
テストをどのように運用するか
テストをオールグリーンに保つためにはコストがかかりますから、プロジェクトの実情に合わせて「何をどこまでテストするか」のルール決めが重要です。
今回のように、プロジェクトの途中からユニットテストを追加していくような場合、その労力は半端ではありません。
また、担当者、チームメンバの経験によっては、テストに対する理解が得られにくいこともあります。
しかしながら、テストの重要性を認識していながら、レガシーなコードに戻ることもできません。
今回の落としどころとして、こんな感じになりました。
- 新規機能はなるべくテストを書く
- サービスレイヤーのみテスト対象とする
- 改修が発生する場合、テストを書いてから改修するように務める
おわりに
それでもテストは書きましょう。
参考
JMockit使い方メモ
http://qiita.com/opengl-8080/items/a49d4dae9067413ccdd6
最強モックツール JMockit その1
http:// genesis-tdsg.blogspot.jp/2013/08/jmockit.html
現在は削除