JUnitの混乱:「extends TestCase」または「@Test」を使用しますか?


152

JUnitの適切な使用(または少なくともドキュメント)は非常に混乱していることがわかりました。この質問は、将来の参照と実際の質問の両方として機能します。

私が正しく理解していれば、JUnitテストを作成して実行するには、主に2つの方法があります。

アプローチA(JUnit 3スタイル): TestCaseを拡張するクラスを作成し、単語でテストメソッドを開始しtestます。(Eclipseで)JUnitテストとしてクラスを実行すると、単語で始まるすべてのメソッドtestが自動的に実行されます。

import junit.framework.TestCase;

public class DummyTestA extends TestCase {

    public void testSum() {
        int a = 5;
        int b = 10;
        int result = a + b;
        assertEquals(15, result);
    }
}

アプローチB(JUnit 4スタイル):「通常の」クラスを@Test作成し、メソッドに注釈を付加します。メソッドを単語で始める必要はないことに注意してくださいtest

import org.junit.*;
import static org.junit.Assert.*;

public class DummyTestB {

    @Test
    public void Sum() {
        int a = 5;
        int b = 10;
        int result = a + b;
        assertEquals(15, result);
    }
}

2つを混合することは良い考えではないようです。たとえば、次のstackoverflowの質問を参照してください。

今、私の質問:

  1. 推奨されるアプローチは何ですか?または、どちらを代わりに使用するのですか?
  2. アプローチBでは、のように@Testアノテーションを拡張することで、例外をテストできます@Test(expected = ArithmeticException.class)しかし、アプローチAを使用する場合、例外をどのようにテストしますか?
  3. アプローチAを使用する場合、次のようにテストスイートにいくつかのテストクラスをグループ化できます。

    TestSuite suite = new TestSuite("All tests");
    suite.addTestSuite(DummyTestA.class);
    suite.addTestSuite(DummyTestAbis.class);

    ただし、これはアプローチBでは使用できません(各テストクラスはTestCaseをサブクラス化する必要があるため)。アプローチBのテストをグループ化する適切な方法は何ですか?

編集:私は両方のアプローチにJUnitバージョンを追加しました


私は見てきたがextends TestCase、各テストも@Test混乱を招くためだけに注釈が付けられている。:)
EMクリエーション

回答:


119

区別はかなり簡単です:

  • 拡張TestCaseはユニットテストがJUnit 3で書かれた方法です(もちろん、JUnit 4でもサポートされています)
  • @Testアノテーションの使用は、JUnit 4で導入された方法です

JUnit 3(および/またはJava 5より前のJavaバージョン)との互換性が必要でない限り、通常はアノテーションパスを選択する必要があります。新しい方法にはいくつかの利点があります。

JUnit 3で予期される例外をテストTestCaseするには、テキストを明示的にする必要があります。

public void testMyException() {
  try {
    objectUnderTest.myMethod(EVIL_ARGUMENT);
    fail("myMethod did not throw an Exception!");
  } catch (MyException e) {
    // ok!
    // check for properties of exception here, if desired
  }
}

JUnit 5はさらに別のAPI変更を導入しましたが、まだ注釈を使用しています。新しい@Testアノテーションはorg.junit.jupiter.api.Test(「古い」JUnit 4のものでしたorg.junit.Test)ですが、JUnit 4のものとほとんど同じように機能します。


わかりやすく丁寧な回答ですが、「例外メッセージの確認」がよくわかりません。ハードコードされた文字列をチェックすることは、メンテナンスの悪夢になるでしょう。「特定の例外タイプのプロパティをチェックする」ことを意味しているはずです。
thSoft

3
@thSoft:使用されることはあまりありませんが、たとえば、例外メソッドが問題のあるフィールドに言及していることを確認したい場合があります。次に、シンプルなものassertTrue(e.getMessage().contains("foo"))が役に立つかもしれません。
Joachim Sauer

1
JUnit4でも、メッセージまたは例外のその他のプロパティ(原因など)を確認する必要がある場合、これは重要なイディオムです。このexpectedメソッドはタイプのみをチェックします。
Yishai

@Yishai:それは本当ですが、問題のある入力に対してメソッドが正しいタイプの例外をスローする場合、ほとんどの場合、すでに満足しています。
Joachim Sauer

そのため、JUnit 5は例外テストに重大な変更を加えました。assertThrows()は素晴らしいです:-)
マーカスK.

25

JUnit 4(アノテーションアプローチ)の方が柔軟性があるため、私はそれを好みます。

JUnit 4でテストスイートを構築する場合は、次のようにすべてのテストをグループ化するクラスを作成する必要があります。

import org.junit.runner.RunWith;
import org.junit.runners.Suite;
import org.junit.runners.Suite.SuiteClasses;


@RunWith(Suite.class)
@SuiteClasses({
    Test1.class,
    Test2.class,
    Test3.class,
    Test4.class
})public class TestSuite
{
 /* empty class */
}

15

あなたの質問には未回答の部分があり、それは「アプローチBのテストをグループ化する適切な方法は何ですか?」です。

公式の答えは、@ RunWith(Suite.class)でクラスに注釈を付け、次に@ Suite.SuiteClasses注釈を使用してクラスをリストすることです。これは、JUnit開発者が行う方法です(スイート内のすべてのクラスを手動でリストします)。多くの点でこのアプローチは改善です。スイート前およびスイート後の動作を追加することは簡単で直感的です(@BeforeClassおよび@AfterClassメソッドを@RunWithで注釈が付けられたクラスに追加するだけです-古いTestFixtureよりもはるかに優れています)。 )。

ただし、注釈ではクラスのリストを動的に作成できず、その問題を回避するには少々醜くなります。Suiteクラスをサブクラス化し、サブクラスでクラスの配列を動的に作成してSuiteコンストラクターに渡す必要がありますが、これはSuiteの他のサブクラス(Categoryなど)が機能せず、本質的には不完全なソリューションです動的Testクラスコレクションをサポートしません。


1
+1。TestSuiteにTestsを追加するための動的ソリューションを作成するタスクに着手した後、各テストでTestCaseを拡張する必要がありました。これは、JUnit4アノテーションを使用して予期される例外を定義する以前に機能していた単体テストを壊しました。動的テストスイートを移入する方法のための私の検索では、このスレッドに私を導いており、具体的には、私は信じてあなたの答えは、3のJUnitを続行するには、いくつかの残りの望ましい理由の一つです
8bitjunkie

4

JUnit 4を使用する必要があります。

多くのフレームワークがJUnit 3.8サポートを廃止するようになりました。

これはSpring 3.0リファレンスドキュメントからです:

[警告]レガシーJUnit 3.8クラス階層は廃止されました

一般に、何か新しいことを始めるときは、常にフレームワークの最新の安定版リリースを使用するようにしてください。


1
  1. 「推奨される」アプローチは、Junit 4以降に導入されたアノテーションを使用することです。これらにより、多くのことが簡単になります(2番目の質問を参照)。

  2. そのために単純なtry / catchブロックを使用できます。


public void testForException() {
    try {
        Integer.parseInt("just a string");
        fail("Exception should have been thrown");
    } catch (final Exception e) {
        // expected
    }
}
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.