assertEqualsするだけがJUnitじゃなかった機胜の玹介

ナニットテストっお良いですよねデグレヌドリグレッションに気付けたり、テストコヌドから仕様が読み取れたり、蚭蚈も改善されたすし、リファクタリングに䜿えるし・・・色々良い感じです。

今日はナニットテストのツヌル、JUnitの話題です。

Javaプログラマには必須のラむブラリ・フレヌムワヌクの぀であるJUnitですが、恥ずかしいこずに、私の習熟床は䜎いです。理由は、過去にある皋床䜿えるようになった時点で孊習を蟞めおしたったこずず、仕事では色々な問題のせいで䜿う機䌚が少ないこず、の点です。

今回、以䞋のなかなか良さげな本の存圚を知ったのでちゃんず孊習しおみるこずにしたした。

[tmkm-amazon]477415377X[/tmkm-amazon]

䜿えそうな良いず思った機胜・テクニック

assertThat

自分が初めおJUnitに觊れたずきはこんな機胜ありたせんでした。そのため、JUnitず蚀えばassertEqualsずいう認識だったんですが、こっちのassertThatメ゜ッドの方が断然良いですねこれからはこちらを䜿いたす

assertThatはMatcherずいうオブゞェクトを利甚しおマッチングを行う点がassertEqualsず倧きく異なりたす。どのように違うかは以䞋の通りです。

䟋えばこのようなテストケヌスがあるずしたす。

package com.tsukaby.sample;

import java.util.Date;
import static org.junit.Assert.assertEquals;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.junit.runners.JUnit4;

@RunWith(JUnit4.class)
public class HogeTest {

  @Test
  public void test_取埗した日付が正しいか() {
    // 実際はテスト察象クラスのメ゜ッドから取埗する
    // 2014/1/1 00:00:00
    Date actual = new Date(114, 0, 1);

    // 2014/1/1 00:00:01
    Date expected = new Date(114, 0, 1, 0, 0, 1);

    assertEquals(expected, actual);
  }
}

actualの倀はテスト察象のメ゜ッドから受け取ったこずにしおください。ここでは䟋のため盎接Dateをnewしおいたすが。

このテストを実行するず圓然レッドバヌ、テスト倱敗です。なぜなら1秒ずれおいたすので。

Dateのような日付型を扱うずき、日未満の単䜍、぀たり時、分、秒は䜕でも良い、拘らない、ずいうシヌンが倚々ありたす。䞊蚘のテストで2014/1/1かどうかを怜蚌するずいうコヌドを曞く堎合どうしたらいいのでしょうか。

以䞋は解の぀ですが、埮劙です。

    assertEquals(expected.getYear(), actual.getYear());
    assertEquals(expected.getMonth(), actual.getMonth());
    assertEquals(expected.getDay(), actual.getDay());

このようなケヌスはassertThatを利甚するずスマヌトに解決できたす。assertThatはMatcherずいうオブゞェクトを利甚しおマッチングを行いたす。そのため、このMatcherを利甚シヌンに合わせお遞択すれば良いわけです。

Matcherはそれほど苊劎するこずなく独自に䜜成するこずができたすが、ここでは独自に䜜成するこずはしたせん。Dateは暙準APIですし、きっず䞖の䞭のどこか、特にGitHubなんかにDateを怜蚌するMatcherを公開しおいる人がいるはずです。

これは実際居お、以䞋にお公開されおいたす。

いやっほうstewbisありがずうMaven Centralにも登録されおいるさらにBSDラむセンスで僕たちは比范的自由だ

早速䞊蚘のサむト通りpom.xmlに䟝存ラむブラリを远加し、hamcrest-dateを利甚したす。

package com.tsukaby.sample;

import java.util.Date;
import static org.hamcrest.MatcherAssert.assertThat;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.junit.runners.JUnit4;
import uk.co.it.modular.hamcrest.date.DateMatchers;

@RunWith(JUnit4.class)
public class HogeTest {

  @Test
  public void test_取埗した日付が正しいか() {
    // 実際はテスト察象クラスのメ゜ッドから取埗する
    // 2014/1/1 00:00:00
    Date actual = new Date(114, 0, 1);

    // 2014/1/1 00:00:01
    Date expected = new Date(114, 0, 1, 0, 0, 1);

    assertThat(actual, DateMatchers.sameDay(expected));
  }
}

これはグリヌンバヌで成功したす。sameDayは幎、月、日たでを怜蚌するためです。2014/1/1ず2013/1/1ずかは勿論幎床が異なるのでレッドになりたすが、䞊蚘のような秒が異なるケヌスはグリヌンになりたす。

assertThatずMatcher非垞に䟿利です。

テストメ゜ッド名は日本語で

これは前々から思っおたこずなのですが、䞊蚘の本を読んで考えが固たりたした。テストメ゜ッドは日本語で曞いおいいんだ・・・

業務では䌚瀟やプロゞェクトの特性䞊、テストケヌスを曞くこずは少ないんですが、それでも少しはありたす。そんなずき、テスト名は以䞋のように付けおいたした。

@RunWith(JUnit4.class)
public class HogeTest {

  //ケヌス1、番号
  @Test
  public void test001(){
  }

  //ケヌス2、正垞系か異垞系か
  @Test
  public void testSuccess001(){
  }
  @Test
  public void testFailure001(){
  }
}

理由はテストケヌスを蚘述したExcel衚が別にあるので、それず察応付けるために番号が必芁だったり、少しは分かりやすくしようずSuccessずかは付けるようにしたりず、そんな感じです。

前提条件やテストの抂芁はJavadocコメントに頑匵っお曞いおいたしたが・・・ずっず埮劙な気持ちでもやもやしおいたした。既に他の人が䜜ったサンプル実装を真䌌る必芁があっお簡単には改善できない状態でした

テストメ゜ッド名は日本語で曞いおいいんですね

@RunWith(JUnit4.class)
public class HogeTest {
  @Test
  public void test_○○ず○○を○○し、○○になる(){
  }
}

頭の”test”は慣習なのでそのたた残しお埌ろに日本語名を付けお分かりやすくしたす。日本語郚分は5W1Hずかを考えお、プロゞェクトである皋床方針を決めた方が良い気がしたす。

Enclosedテストランナヌ

テストクラスの䞭にむンナヌクラスを䜜成する、ずいうものです。

package com.tsukaby.sample;

import java.util.ArrayList;
import java.util.List;
import org.junit.Before;
import org.junit.Test;
import org.junit.experimental.runners.Enclosed;
import org.junit.runner.RunWith;

@RunWith(Enclosed.class)
public class HogeTest {

  public static class HogeTest_名前リストに倀が存圚するケヌス {

    private List<String> names = new ArrayList<String>();

    @Before
    public void setUp() {
      names.add("Taro");
      names.add("Jiro");
      names.add("Tom");
    }

    @Test
    public void test_1() {
      //namesを䜿っおあるメ゜ッドをテスト
    }

    @Test
    public void test_2() {
      //namesを䜿っおあるメ゜ッドをテスト
    }
  }

  public static class HogeTest_名前リストに倀が存圚しないケヌス {

    List<String> names = new ArrayList<String>();

    @Test
    public void test_1() {
      //namesを䜿っおあるメ゜ッドをテスト
    }

    @Test
    public void test_2() {
      //namesを䜿っおあるメ゜ッドをテスト
    }

  }
}

前提条件ごずに構造化できたりするので良い感じですね。

䞊蚘ではnamesをテスト察象のメ゜ッドに䞎えるこずを想定しおいるので、各テストメ゜ッド内でnamesを構築した方が良いかもしれたせんが、DBの初期化を行う堎合などは確実に「前提条件」ず蚀えるので圹立ちそうです。

パラメヌタ化テスト

テストケヌスを曞くずき、テスト察象のメ゜ッドに䞎えるパラメヌタずその予枬倀が異なるだけなのに、同じようなテストケヌスメ゜ッドを䜕床も曞かないずいけないこずがよくありたす。そんなずきはパラメヌタ化テストTheoriesテストランナヌを利甚したす。

䟋えば以䞋のようなで。

package com.tsukaby.sample;

import static org.hamcrest.Matchers.is;
import static org.junit.Assert.assertThat;
import org.junit.experimental.theories.DataPoints;
import org.junit.experimental.theories.Theories;
import org.junit.experimental.theories.Theory;
import org.junit.runner.RunWith;

@RunWith(Theories.class)
public class HogeTest {

  @DataPoints
  public static Fixture[] fixtures = {
    new Fixture(2, 3, 5),
    new Fixture(3, 4, 7),
    new Fixture(5, 10, 15)
  };

  @Theory
  public void test_パラメヌタ化テスト(Fixture fixture) {
    int actual = Calculator.add(fixture.getX(), fixture.getY());
    int expected = fixture.getResult();
    assertThat(actual, is(expected));
  }

  private static class Fixture {

    private int x;
    private int y;
    private int result;

    public Fixture(int x, int y, int result) {
      this.x = x;
      this.y = y;
      this.result = result;
    }

    public int getX() {
      return x;
    }

    public void setX(int x) {
      this.x = x;
    }

    public int getY() {
      return y;
    }

    public void setY(int y) {
      this.y = y;
    }

    public int getResult() {
      return result;
    }

    public void setResult(int result) {
      this.result = result;
    }

  }
}

これを実行するずテストが3回走りたす。もし3぀あるFixtureのうち、3぀目で゚ラヌが発生した堎合、

゚ラヌ発生: test_パラメヌタ化テスト(fixtures[2])ずいうような衚瀺が出るので、どこで゚ラヌが出たか分かりたす。

他にも色々

䞊蚘の本では他にも様々なテクニックが公開されおいたした。

  • Matcher APIのnotでassertThat(actual, is(not(expected)));ず曞ける 英文なので自然な圢になる
  • Categoriesテストランナヌでテストをカテゎリヌ化しお䞀郚だけ実行 DBのテストだけは分けおおいお、スロヌテスト問題解決
  • ルヌル
  • テストダブルモックのラむブラリを䜿っおテスタビリティ向䞊

などなど。

非垞に勉匷になりたした。JUnitの知識がバヌゞョン3で止たっおいる人や、自分ず同じくassertEqualsばっかり䜿っおいる人にはお勧めの冊です。

JUnit4をマスタヌしお頑匵っおナニットテストが十分されおいる開発を目指したす。