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設定画面 JMockitのビルドパスを調整
Eclipse設定画面 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オプションを指定してください。

オプションの設定方法については以下をご覧ください。

[Java]JMockitのカバレッジオプションをpom.xmlに定義する
新さっぽろ IT キャリア研究室

テストをどのように運用するか

テストをオールグリーンに保つためにはコストがかかりますから、プロジェクトの実情に合わせて「何をどこまでテストするか」のルール決めが重要です。

今回のように、プロジェクトの途中からユニットテストを追加していくような場合、その労力は半端ではありません。
また、担当者、チームメンバの経験によっては、テストに対する理解が得られにくいこともあります。

しかしながら、テストの重要性を認識していながら、レガシーなコードに戻ることもできません。

今回の落としどころとして、こんな感じになりました。

  • 新規機能はなるべくテストを書く
  • サービスレイヤーのみテスト対象とする
  • 改修が発生する場合、テストを書いてから改修するように務める

おわりに

それでもテストは書きましょう。

参考

JMockit使い方メモ
http://qiita.com/opengl-8080/items/a49d4dae9067413ccdd6

最強モックツール JMockit その1
http:// genesis-tdsg.blogspot.jp/2013/08/jmockit.html
現在は削除


実践 JUnit ―達人プログラマーのユニットテスト技法

Jeff Langr,Andy Hunt,Dave Thomas
出版社:オライリージャパン  発売日:2015-09-02

Amazonで詳細を見る

チーム開発実践入門 ~共同作業を円滑に行うツール・メソッド (WEB+DB PRESS plus)

池田 尚史,藤倉 和明,井上 史彰
出版社:技術評論社  発売日:2014-04-16

Amazonで詳細を見る

この記事の著者 Webrow (うぇぶろう)
Web アプリ開発、 Web 顧問 エンジニア、WordPress サポートいたします。