Java JUnitを使ってみる

JUnitってなんぞや~?

ワンパターンな入りですみません...。

 JUnitとはJavaで開発されたプログラムにおいてユニットテスト単体テスト)の自動化を行うためのフレームワークである。

JUnit - Wikipedia

Eclipseには、標準でJUnitが導入されているようです。

それでは、実際に使っていきたいと思います。 

JUnitを使ってみる

まずは、プロジェクトを作成します。「ファイル」>「新規」>「Javaプロジェクト」 

f:id:ts0818:20170717183526j:plain

「プロジェクト名(P):」を入力し、「完了(F)」をクリック。

f:id:ts0818:20170717183527j:plain

次に、プロジェクト名の上で右クリックし、「新規」>「クラス」をクリック。

f:id:ts0818:20170717183528j:plain

「パッケージ(K):」「名前(M):」を入力し、「完了(F)」をクリック。

f:id:ts0818:20170717183529j:plain

足し算メソッドを実装しておきます。

package sampleJUnit;

public class CalcAdd {
  public int add(int a, int b) {
	  return a + b;  
  }
}

次に、テスト用のクラスを作成します。まず、フォルダを作成します。

「新規」>「ソース・フォルダー」 をクリック。

f:id:ts0818:20170717203610j:plain

「フォルダー名(D):」を入力し、「完了(F)」をクリック。 

f:id:ts0818:20170717203605j:plain

プロジェクト名の上で右クリックし、「ビルド・パス(B)」>「ビルド・パスの構成(C)...」をクリック。

f:id:ts0818:20170717203606j:plain

「ライブラリーの追加(I)...」をクリック。

f:id:ts0818:20170717203607j:plain

JUnit」を選択し、「次へ(N)>」をクリック。

f:id:ts0818:20170717203608j:plain

JUnit ライブラリー・バージョン(J):」で「JUnit 4」を選択し、「完了(F)」をクリック。 

f:id:ts0818:20170717203609j:plain

JUnit 4」が追加されていればOK。「OK」をクリック。 

f:id:ts0818:20170717203611j:plain

プロジェクト名の上で右クリックし、「新規」>「その他(O)...」をクリック。

f:id:ts0818:20170717203612j:plain

Java」>「JUnit」>「JUnitテスト・ケース」を選択し、「次へ(N)」をクリック。

f:id:ts0818:20170717203613j:plain

「名前(M):」を入力し、「完了(F)」をクリック。

f:id:ts0818:20170717203614j:plain

テストのクラスができます。

f:id:ts0818:20170717203615j:plain

テストクラスを書き換えます。

package sampleJUnit;

import static org.junit.Assert.*;

import org.junit.Test;

public class CalcAddTest {

  @Test
  public void testCase() {
    CalcAdd add = new CalcAdd();
    int result = add.add(1, 2);
    
    // 計算の結果
    assertEquals(result, 3);
  }

}

テストを実行します。テストクラス(CalcAddTest.java)を選択した状態で、f:id:ts0818:20170714224011j:plainをクリック。

f:id:ts0818:20170717203616j:plain

「OK」をクリックします。

f:id:ts0818:20170717203617j:plain

エラーに!「OK」を選択。

f:id:ts0818:20170717203618j:plain

「テスト・ランナー(T):」が「JUnit 3」になってるので、

f:id:ts0818:20170717203619j:plain

「テスト・ランナー(T):」が「JUnit 4」に変えてみて、「実行(R)」をクリック。

f:id:ts0818:20170717203620j:plain

エラ~っ!!!!

f:id:ts0818:20170717203621j:plain

単体テストの実行(I)」にチェックをし、「テスト・クラス(E):」に作成したテストクラスを指定。「実行」をクリック。

f:id:ts0818:20170717203622j:plain

JUnit」の結果が表示されました。testCase()メソッドの結果が表示されています。「エラー」「失敗」ともに0で、「障害トレース」にも何も表示されていないため、バグはないことが分かりました。

f:id:ts0818:20170717203623j:plain

テストメソッドを追加し、失敗の場合も見ていくことにします。 

package sampleJUnit;

import static org.junit.Assert.*;

import org.junit.Test;

public class CalcAddTest {

  @Test
  public void testCase() {
    CalcAdd add = new CalcAdd();
    int result = add.add(1, 2);
    
    // 計算の結果
    assertEquals(result, 3);
  }
  
  @Test
  public void testCaseFail() {
    CalcAdd add = new CalcAdd();
    int result = add.add(1, 2);
    
    // 計算の結果
    assertEquals(result, 2);
    
  }

}

再び、実行してみます。

f:id:ts0818:20170717203624j:plain

testCaseFail()メソッドでエラーが起こりました。「障害トレース」をコピーしエディターなどに張り付けてみると、

java.lang.AssertionError: expected:<3> but was:<2>
	at org.junit.Assert.fail(Assert.java:88)
	at org.junit.Assert.failNotEquals(Assert.java:834)
	at org.junit.Assert.assertEquals(Assert.java:645)
	at org.junit.Assert.assertEquals(Assert.java:631)
	at sampleJUnit.CalcAddTest.testCaseFail(CalcAddTest.java:24)
	at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
	at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62)
	at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
	at java.lang.reflect.Method.invoke(Method.java:498)
	at org.junit.runners.model.FrameworkMethod$1.runReflectiveCall(FrameworkMethod.java:50)
	at org.junit.internal.runners.model.ReflectiveCallable.run(ReflectiveCallable.java:12)
	at org.junit.runners.model.FrameworkMethod.invokeExplosively(FrameworkMethod.java:47)
	at org.junit.internal.runners.statements.InvokeMethod.evaluate(InvokeMethod.java:17)
	at org.junit.runners.ParentRunner.runLeaf(ParentRunner.java:325)
	at org.junit.runners.BlockJUnit4ClassRunner.runChild(BlockJUnit4ClassRunner.java:78)
	at org.junit.runners.BlockJUnit4ClassRunner.runChild(BlockJUnit4ClassRunner.java:57)
	at org.junit.runners.ParentRunner$3.run(ParentRunner.java:290)
	at org.junit.runners.ParentRunner$1.schedule(ParentRunner.java:71)
	at org.junit.runners.ParentRunner.runChildren(ParentRunner.java:288)
	at org.junit.runners.ParentRunner.access$000(ParentRunner.java:58)
	at org.junit.runners.ParentRunner$2.evaluate(ParentRunner.java:268)
	at org.junit.runners.ParentRunner.run(ParentRunner.java:363)
	at org.eclipse.jdt.internal.junit4.runner.JUnit4TestReference.run(JUnit4TestReference.java:86)
	at org.eclipse.jdt.internal.junit.runner.TestExecution.run(TestExecution.java:38)
	at org.eclipse.jdt.internal.junit.runner.RemoteTestRunner.runTests(RemoteTestRunner.java:459)
	at org.eclipse.jdt.internal.junit.runner.RemoteTestRunner.runTests(RemoteTestRunner.java:678)
	at org.eclipse.jdt.internal.junit.runner.RemoteTestRunner.run(RemoteTestRunner.java:382)
	at org.eclipse.jdt.internal.junit.runner.RemoteTestRunner.main(RemoteTestRunner.java:192)

結構な行数になりますが、

java.lang.AssertionError: expected:<3> but was:<2>    
at sampleJUnit.CalcAddTest.testCaseFail(CalcAddTest.java:24)

「障害トレース」に表示されている部分に着目すれば良いようです。

JUnitで使われるアノテーション

その中でも、よく使われるものがあるようです。

@Test アノテーション 

そのメソッドがテスト実行対象のメソッド(=テストメソッド)であることを宣言します。テスト実行時に自動的に呼び出されます。

@Test
public void testCase() {
  CalcAdd add = new CalcAdd();
  int result = add.add(1, 2);
  // 計算結果の比較
  assertEqauls(result, 3);
}

@Befor アノテーション 

そのメソッドが、各テストメソッドの実行前に呼び出されることを宣言します。テストメソッドの初期化処理を表すために利用されます。

@Before
public void setup() {
  // テスト初期化処理を実施
  init();
}

@After アノテーション 

そのメソッドが、各テストメソッドの実行後に呼び出されることを宣言します。テストメソッド実効後の後処理を表すために利用されます。

@After
public void tearDown() {
  // テスト終了後の処理を実施
  refresh();
}

@BeforeClass アノテーション 

そのメソッドが、すべてのテストメソッドの実行前に、1度だけ呼び出されることを宣言します。このアノテーションを付けるメソッドはstaticとして宣言する必要があります。

@BeforeClass
public static void setupClass() {
  // テストクラス起動時の初期化処理を実施
  init();
}

@AfterClass アノテーション 

そのメソッドが、すべてのテストメソッドが実行された後に、1度だけ呼び出されることを宣言します。このアノテーションを付けるメソッドはstaticとして宣言する必要があります。

@AfterClass
public static void tearDownClass() {
  // テストクラス終了時の処理を実施
  init();
}

@Ignore アノテーション 

@Ignoreアノテーションを付与したテストメソッドは、実行の対象外となります。テストクラスを作成中に、一時的にテストメソッドの実行をスキップしたい場合に利用します。

@Ignore
@Test
public static void testCaseTemp() {
  CalcAdd add = new CalcAdd();
  int result = add.add(1, 2);
  // 計算結果の比較
  assertEqauls(result, 3);
}

Assertクラス 

org.junit.Assertをインポートすることで利用できます。staticインポートで用いられる?ようです。

import static org.junit.Assert.*;    

assertEqauls メソッド

『検証対象の値』『予想値』が等価であるかを評価します。第1引数に『検証対象の値 』を、第2引数に『予想値』を指定します。第1引数と第2引数が等価である場合、テスト成功となります。

@Test
public void testCaseEqauls() {
  CalcAdd add = new CalcAdd();
  int result = add.add(1, 2);
  
  // 計算結果の比較(第1引数: 検証値, 第2引数: 予想値)
  assertEqauls(result, 3);
}

fail メソッド

failメソッドが呼び出されると、テストは必ず失敗となります。テスト実行中に例外が発生した場合に、このメソッドを呼び出すことで、テストを強制的に失敗させることができます。

@Test
public void testCaseFail() {
  try {
  
  } catch(Exception e) {
    fail("テスト失敗");
  }
}

assertThat メソッド

JUnit 4.4から実装されたメソッドです。第1引数に『検証対象の値 』を、第2引数に『予想値』を指定します。第2引数に Matcher を使って評価方法を決定します。

@Test
public void testCaseThat() {
  CalcAdd add = new CalcAdd();
  int result = add.add(1, 2);
  
  // 計算結果の比較(第1引数: 検証値, 第2引数: 予想値)
  assertThat(result, is(3));
}
    

is() の部分がMatcherで、Matcherを変更することで、評価方法を変更できるようです。

Matcherクラス

org.hamcrest.CoreMatchers をインポートすることで利用可能です。staticインポートで用いられる?ようです。

import static org.hamcrest.CoreMatchers.*;   

is メソッド

検証対象の値と予想値の値が等価であるかを検証します。

//  計算結果の比較(第1引数: 検証値, 第2引数: 予想値)
assertThat(result, is(3));

テストの作り方が難しいところです。

とりあえず、Eclipseを使っていれば、JUnitのライブラリは標準でインストールされているので、外部からインストールしてくる必要はないようです。