JUnit 予期される例外テスト: @Test(expected)

⚡ スマートサマリー

JUnit 例外テストでは、@Test アノテーションのオプションの期待パラメータを使用して、メソッドがスローするはずのエラーをスローすることを確認します。 JUnit 4 および assertThrows メソッド JUnit 5.

  • 🔘 目的: 無効な入力に対して、誤った回答を黙って表示するのではなく、文書に記載されている例外が発生することを証明する。
  • ☑️ 構文: @Test(expected = ArithmeticException.class) は、その正確な例外タイプがスローされた場合にのみ合格します。
  • 例: JUnitメッセージがゼロ除算され、AirthematicTest は結果として ArithmeticException が発生することを想定しています。
  • 🧪 代替: tryブロックの後にfail()を実行すると、例外を検証する前に、捕捉した例外を検査できます。
  • 🛠️ JUnit 5: assertThrowsは捕捉した例外を返すため、メッセージと原因もアサートできます。
  • 📌 落とし穴: 例外が発生した箇所では、期待されるパラメータがそのまま渡されるため、セットアップコードの不具合が隠蔽される可能性があります。

JUnit @Test(expected) パラメータを使用した予期された例外テスト

JUnit 機能を提供する trac例外を発生させ、コードが期待される例外をスローしているかどうかを確認する。

JUnit 4は、例外テストを簡単かつ読みやすく行う方法を提供します。以下を使用できます。

  • オプションパラメータ(期待値) @Test アノテーション,
  • に trac情報があれば、「fail()」を使用できます。

一方、 テスト 例外が発生した場合、そのオプション パラメーターで指定する例外クラスが @Test アノテーション これは、メソッドが実際にスローする例外と同じです。これは、メソッドから例外が発生することを期待しているためです。 単体テストそうでなければ、 JUnit テストは失敗するでしょう。

例: @Test(expected = IllegalArgumentException.class)

「expected」パラメータを使用すると、テストで発生する可能性のある例外名を指定できます。上記の例では、「不正な引数例外これは、開発者が許可されていない引数を使用した場合にテストによってスローされます。

@Test(expected) を使用した例

例外テストを理解するために、 Java 例外をスローするメソッドを持つクラス 例外テストクラスでそれを処理してテストします。 JUnitMessage.java には、数学演算を実行するメソッドがあります。14 行目の除算はゼロ除算になるため、このメソッドは常に「ArithmeticException」をスローします。以下を参照してください。

JUnitメッセージクラス Eclipse printMessage の 14 行目でゼロ除算が発生しています。

package guru99.junit;

public class JUnitMessage{

	private String message;

	public JUnitMessage(String message) {
		this.message = message;
	}

public void printMessage(){

	System.out.println(message); 
	int divide=1/0;

}

public String printHiMessage(){ 

	message="Hi!" + message;
	
	System.out.println(message);

	return message;
}

}

Code 説明:

  • Code ライン7: フィールド初期化を使用してパラメーター化されたコンストラクターを作成します。
  • Code 11〜14行目: 数学演算を行うための方法を作成する。
  • Code ライン18: メッセージを出力する別のメソッドを作成します。
  • Code ライン20: メッセージを出力するための新しい文字列を作成します。
  • Code ライン22: 20行目で作成された新しいメッセージを印刷します。

上記のテストクラスを作成しましょう Java 例外を検証するためのクラス。

以下に、上記の例外(ここでは ArithmeticException)を単体テストするテストクラスを示します。 Java クラス:

AirthematicTest.java

以下のスクリーンショットは、エディタで同じテストを実行した様子を示しています。ファイルはAirthematicTest1として保存され、13行目で期待されるパラメータがハイライト表示されています。

AirthematicTest1 クラス Eclipse 13行目で@Test(expected = ArithmeticException.class)が強調表示されています。

package guru99.junit;

import static org.junit.Assert.assertEquals;

import org.junit.Test;

public class AirthematicTest {

	public String message = "Saurabh";
	
	JUnitMessage junitMessage = new JUnitMessage(message);
	
	@Test(expected = ArithmeticException.class)
	public void testJUnitMessage(){

		System.out.println("Junit Message is printing ");
		junitMessage.printMessage();

	}

	@Test
	public void testJUnitHiMessage(){ 
		message="Hi!" + message;
		System.out.println("Junit Message is printing ");
		assertEquals(message, junitMessage.printHiMessage());
	
	}
}

Code 説明:

  • Code ライン13: @Test アノテーションを使用してテストを作成します。上記のクラスのメソッドを実行すると、数学演算が呼び出されます。ここでは ArithmeticException が想定されるため、@Test のパラメータとして指定します。
  • Code ライン17: printMessage() を呼び出しています JUnitメッセージ.java。
  • Code 21〜22行目: 「Hi」メッセージをチェックする別のテストメソッドを作成します。今回は、想定されるパラメータは含めません。

このクラスには2つのテストメソッドが含まれているため、1回の実行で両方が実行されます。1つはArithmeticExceptionを期待するメソッド、もう1つは返された文字列に対してアサートを行うメソッドです。

注意: この例は、元の資料では3つの名前で表記されています。リストではクラス名がAirthematicTest、エディタのスクリーンショットではAirthematicTest1、結果ビューではJunitTestExampleと表示されます。コードはすべて同じで、ファイル名だけが異なります。

実行して結果を確認しましょう。 JUnit 以下は、 JunitTestExample.java.

出力:

以下が出力結果です。テストは失敗なく成功しました。 traceは以下に示すとおりです。

Eclipse JUnit レポートを表示 JUnitTestExample は 2/2 で実行され、エラーと失敗はゼロです

どちらの方法も合格です。最初の方法は宣言した ArithmeticException が発生したため合格し、2 番目の方法は返された文字列が一致したため合格します。除算がスローされなかった場合は、 JUnit 最初のメソッドは「予期された例外: java.lang.ArithmeticException」というメッセージで失敗していたでしょう。

例外をテストする 3 つの方法 JUnit 4

期待されるパラメータは3つのうち最短である JUnit 4つの慣用句がありますが、必ずしも正しいとは限りません。この表では、選択を決定する2つの質問、つまりメッセージについて断言できるか、どの行が使われたかを知っているか、という点に基づいてそれらを比較しています。

アプローチ そのメッセージは断言しているのでしょうか? 投擲ラインをピンで固定する? ベスト
@Test(expected = X.class) いいえ いいえ、メソッド内のどの行でも例外が発生する可能性があります 例外の種類だけが重要な、短いテスト。
try / fail() / catch はい、キャッチブロック内です はい、監視されている通話のみが監視されています メッセージまたは原因を調査する必要があるテスト。
@Rule 予期された例外 はい、expectMessage() を通じて いいえ 既存のルールに基づいて構築されたレガシースイート。

導入部分で説明されている fail() のイディオムは次のようになります。呼び出しが例外をスローしない場合、fail() が実行され、テストはあなたが書いたメッセージを報告します。

@Test
public void testDivideByZero() {
    try {
        junitMessage.printMessage();
        fail("Expected an ArithmeticException");
    } catch (ArithmeticException e) {
        assertEquals("/ by zero", e.getMessage());
    }
}

ExpectedException ルールは、この 2 つの間に位置します。これは、 JUnit 4.13 次に説明する assertThrows メソッドを支持するため、新しい JUnit 4つのテストでは、それを採用すべきではない。

例外をテストする方法 JUnit assertThrows() を使用した 5

JUnit 5 は @Test から期待されるパラメータを完全に削除します。Jupiter が提供します assertThrows()これは、例外クラスとテスト対象コードを保持するラムダ式を受け取ります。捕捉した例外を返すため、メッセージ、原因、および任意のカスタムフィールドを後でアサートできます。

import static org.junit.jupiter.api.Assertions.assertEquals;
import static org.junit.jupiter.api.Assertions.assertThrows;
import org.junit.jupiter.api.Test;

public class AirthematicJupiterTest {

    JUnitMessage junitMessage = new JUnitMessage("Saurabh");

    @Test
    public void testJUnitMessage() {
        ArithmeticException thrown = assertThrows(
                ArithmeticException.class,
                () -> junitMessage.printMessage());
        assertEquals("/ by zero", thrown.getMessage());
    }
}

3つの関連 アサーション 家族を充実させる:

  • assertThrows 例外型、またはそのサブクラスを受け入れます。
  • assertThrowsExactly サブクラスを拒否するため、指定された型のみが通過します。
  • assertDosNotThrow これは、ブロックが正常に完了するという、正反対の期待を示しています。

その JUnit このページにある 4 つのコードはまだ実行されています JUnit プラットフォームはビンテージエンジンを使用しているため、プロジェクトの移行中に動作を継続させるために、上記のコードを書き直す必要はありません。

例外をテストする際のよくある間違い JUnit

例外テストは、いくつかの明確な方法で失敗します。各行には、症状、原因、および解決策が記載されています。

症状 原因となる 修正する
予期される例外: java.lang.ArithmeticException メソッドは例外をスローすることなく完了しました。 入力が本当に無効であることを確認し、再度実行してください。
テストは合格したが、間違った行が投げられた 想定されるパラメータは、セットアップを含むメソッド全体を監視します。 セットアップを別の場所に移動するか、1 つの呼び出しの周りに assertThrows を使用するように切り替えてください。
エディタで処理されない例外タイプ チェック例外がスローされるが、宣言されない。 テストメソッドのシグネチャに例外処理(throws)を追加します。
意図していなかったサブクラスでテストがパスする assertThrows は、指定された型のサブクラスを受け入れます。 厳密な型一致を求める場合は、assertThrowsExactly を使用してください。
expected は有効な属性ではありません このテストは、Jupiterの@Testアノテーションに対してコンパイルされました。 org.junit.Test をインポートします JUnit 4、または assertThrows に移動します。

最後の行は、移行の途中で多くの人がつまずく箇所です。なぜなら、どちらの注釈も@Testという名前で、インポート部分でしか区別できないからです。ping XNUMXつ JUnit バージョン テストケース このクラスは、問題の種類全体を回避する。

よくあるご質問

想定されるパラメータではメッセージをチェックできません。呼び出しをtryブロックで囲み、直後にfail()を呼び出し、catchブロック内でgetMessage()に対してassertを行ってください。 JUnit 5 では、assertThrows が捕捉した例外を返すため、これがより簡単になります。

JUnit テストは失敗し、「予期された例外」というメッセージに続いて、指定したクラス名が表示されます。それ以外の情報は何も報告されないため、例外をスローしないテストは、本番コードの動作が密かに変更されたテストと全く同じように見えます。

AIアシスタントはメソッドを読み込み、各throw文に到達する入力を一覧表示し、分岐ごとにテストを作成します。出力は出発点として扱います。アシスタントはコードから例外の種類を推測するため、誤ったthrow文はテストに忠実にコピーされます。

副操縦士 ファイル内に既に存在するスタイルに従うため、複数のスタイルが混在するプロジェクトでは、スタイルが混在した状態になります。エディタ上では org.junit.Test と org.junit.jupiter.api.Test が同じように見えるため、提案を受け入れる前にインポート行を確認してください。

それはまだ存在しますが、非推奨になりました JUnit バージョン4.13以降はJupiterには移植されていません。既存のテストスイートはそのまま使用できますが、新しいテストでは、より読みやすく、公開ルールフィールドを必要としないassertThrowsを使用してください。

はい。ラムダパラメータは実行可能オブジェクトであり、Throwable をスローするように宣言されているため、チェック例外にはテストメソッド自体に throws 句は必要ありません。 JUnit 4. トライ・アンド・キャッチの慣用句。

assertThrows は、指定した型の任意のサブクラスを受け入れるため、RuntimeException を期待すると NullPointerException も渡されます。正確なクラスが重要な場合は assertThrowsExactly を使用するか、実際に期待する最も狭い型を指定してください。

例外が契約の一部である場合に限るtract. 例外をテストするメソッドは、意図しない動作でロックをスローすることを決して保証せず、リファクタリングを難しくするため、テストの目的が損なわれます。