Joinしたプロジェクトでユニットテストをしていないと分かったときの絶望感。

ということで、Java + Spring Frameworkという構成のプロジェクトにJMockitでユニットテストとカバレッジレポートを導入します。

構成

  • Java 8
  • Spring Framework 4.1系
  • JUnit 4系
  • JMockit 1.21
  • Eclipse

STSを利用している場合EclipseをSTSと読み替えてください。
ほぼ同じです。

準備

pom.xml

プロジェクトの依存関係にテスト関係のライブラリを追加します。
pom.xmlに以下を追加してください。

<dependencies>
    :
    <dependency>
         <groupId>org.springframework</groupId>
         <artifactId>spring-test</artifactId>
         <version>${spring-ver}</version>
    </dependency>
    <dependency>
        <groupId>org.jmockit</groupId>
        <artifactId>jmockit</artifactId>
        <version>1.21</version>
    </dependency>
    <dependency>
        <groupId>org.jmockit</groupId>
        <artifactId>jmockit-coverage</artifactId>
        <version>1.21</version>
    </dependency>
    <dependency>
        <groupId>junit</groupId>
        <artifactId>junit</artifactId>
        <version>${junit-ver}</version>
        <scope>test</scope>
    </dependency>
    :
<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が読み込まれるようにする。

art-jmockit-build-path

メモ

eclipseのメニュー名称は環境によって若干異なるようですね。

また、Mavenのローカル・リポジトリの場所が分からないという場合はEclipseの設定を開き、[ Maven > ユーザ設定 ]をチェックしてみてください。
通常はホーム直下に.m2があると思います。

私の場合は次の場所でした。(win)

C:\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をクラスを作成する

テストクラスの書き方

// JMockitを動かすためのアノテーション
 @RunWith(JMockit.class)
 public class UserServiceTest {

    // テスト対象のサービスクラスをnewする
    private UserService targetService = new UserServiceImpl();

    // テスト対象クラスで@Autowiredしているものは@Mockedを宣言する
    @Mocked
    private CacheService cacheService;

    // テストではDIされないため、モックしたものをそれぞれDeencapsulation.setField()する
    @Before
    public void setUp() throws Exception {
        Deencapsulation.setField(targetService, cacheService);
    }

    // @Testアノテーションをつけたものがテスト対象と見做される
    @Test
    public void getSkillsTest() {
        MSkill skill1 = new MSkill();
        skill1.setId(17);
        :
        MSkill skill2 = new MSkill();
        skill2.setId(18);
        :

        // テスト対象メソッド内でDIされたインスタンスの処理結果をセットし、任意の状態を作り出す
        // resultは開業せずに同じ行に書くほうが多いようだ
        new NonStrictExpectations() {{
            cacheService.findSkill(17);      result = skill1;
            cacheService.findSkill(anyInt);  result = skill2;
            :
        }};

        // テスト対象メソッドの実行と、結果の検証
        List<UserSkill> skills = targetService.getSkills(888L);
        assertEquals(skills.size(), 2);
        assertEquals(skills.get(0).getId(), skill1.getId());
        :
    }

    // プライベートなメソッドのテストではリフレクションを用いる
    @Test
    public void calcHpTest() throws Exception {
        Method method = UserServiceImpl.class.getDeclaredMethod(
            "calcHp",
            Integer.class,
            int.class,
            int.class
        );
        method.setAccessible(true);

        assertEquals((Integer)method.invoke(targetService, 1, 3, 2), new Integer(2));
        :
    }
}

テストの実行方法

eclipseからテストを実行する

  • 1.eclipseのパッケージエクスプローラーからプロジェクトのトップレベルを右クリック
  • 2.[実行(Run As) -> Maven test ]を選択する

ターミナルからテストを実行する

  • pom.xmlがある位置までcdする
  • mvn testとコマンドを入力し、実行する

カバレッジをチェックする

通常、実行パス直下にcoverage-reportディレクトリが生成されます。

カバレッジの緑色の部分がテストで実行されているコードです。
なるべく緑色を増やして網羅率を上げていきましょう。

任意のディレクトリに出力する場合は、coverage-outputDirオプションを指定してください。

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

[Java]JMockitのカバレッジオプションをpom.xmlに定義する

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

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

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

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

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

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

おわりに

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

この記事はtomita@atuwebがお届けしました。

参考

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

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




2016年6月28日:オプションについて追記しました。

スポンサーリンク
ad_336
ad_336
  • このエントリーをはてなブックマークに追加
  • Evernoteに保存Evernoteに保存
スポンサーリンク
ad_336