Java の単体テストとは何ですか?
Java での JUnit の学習に入る前に、単体テストとは何か、また単体テストが人気がある理由を簡単に説明します (このことをすでに知っている場合は、「Java で JUnit テストを作成するにはどうすればよいですか?」までスキップしてください)。Java での単体テストにより、大規模なソフトウェア開発がより効率的かつ楽になります。個人とチームの両方がデバッグにかかる膨大な時間を削減し、コラボレーション プロセスを大幅に合理化するのに役立ちます。
https://junit.org/junit4/
単体テストの基本的な考え方は次のとおりです。個々の機能のアトミック テスト (単体テストと呼ばれます) を作成し、前の機能が機能することをテストして確認した後、ゆっくりと機能を追加します。非常にシンプルですが強力なアイデアです。このプロセスがどのようになるかの例として、仮想関数電卓を構築していると想像してください。
+
この電卓は、見かけの算術演算子 ( 、
-
、
x
、 )に加えて
%
、内部で動作する他のサブ機能を必要とする高度な機能を備えています。指数を計算するには、電卓が正しく乗算できる必要があります。したがって、この計算機を構築してテストする単体テストのアプローチは次のようになります。
- 加算関数を書きます。慎重にテストし、変更し、機能するまで繰り返します。
- 減算、乗算、除算の関数についても同じことを行います。
- これらの基本演算子を使用して、指数などのより高度な演算子関数を作成し、それらの関数もテストします。
これにより、他の小さなサブ機能から構築された機能がそれ自体で適切に動作するだけでなく、その中に欠陥のあるサブ機能が含まれないことが保証されます。たとえば、指数関数をテストしていて何か問題が発生した場合、乗算関数はすでに広範にテストされているため、おそらく乗算サブ機能にバグはないことがわかります。これにより、バグを見つけるためにバックトレースして検査する必要があるコードの総量が大幅に削減されます。この些細な例によって、単体テストに関する思考プロセスがどのように構造化されているかが明らかになることを願っています。しかし、単体テストは他のソフトウェア開発プロセスとどのように相互作用するのでしょうか? 連携して通信できる必要があるさらに複雑な機能がある場合はどうなるでしょうか? このような複雑な機能が適切に連携して動作することを確認するには、単体テストだけでは不十分です。実際、これはソフトウェア テストの 4 つのレベルの最初のステップにすぎません (業界標準またはソフトウェア テストの最も一般的なアプローチを指すため、大文字を使用しています)。最後の 3 つのステップは、
統合テスト、
システムテスト、および
受け入れテスト。これらはすべて、おそらく皆さんが考えているとおりの意味を持っていると思いますが、明確にしておきます。統合テストは、上記の「複雑な機能」が相互に適切に連携することを保証するために行うものです。(例: 電卓が「3 + 7 * 4 - 2」を処理できるかどうかを確認するなど) システム テストでは、特定のシステムの全体的な設計をテストします。多くの場合、複雑な機能を含む複数のシステムが製品内で連携して動作するため、これらをシステムにグループ化し、個別にテストします。(たとえば、グラフ電卓を構築している場合、まず数値を処理する算術「システム」を構築し、意図したとおりに動作するまでテストします。次に、引き出しを処理するグラフ「システム」を構築してテストします。それは算術システムから構築されます)。受け入れテストはユーザーレベルのテストです。すべてのシステムが同期して動作して、ユーザー (たとえば、計算機をテストするユーザー) に受け入れられる完成品を作成できるかどうかを確認しています。企業では他の従業員にユーザー (ベータ) テストを個別に展開させることが多いため、ソフトウェア開発者はプロセスのこの最終ステップを無視することがあります。
Java で JUnit テストを作成するにはどうすればよいですか?
単体テストの利点と制限についてより明確に理解できたので、いくつかのコードを見てみましょう。JUnit と呼ばれる人気のある Java テスト フレームワークを使用します (もう 1 つの人気のあるフレームワークは TestNG で、必要に応じて使用することもできます。これらは構文的に非常に似ており、TestNG は JUnit からインスピレーションを得ています)。
ここからJUnit をダウンロードしてインストールできます。このコード例では、前に説明した「関数電卓」の例を続けます。理解するのは非常に簡単で、テストコードも非常に簡単です。従来の方法では、クラスごとに個別のテスト クラスを作成するので、それを実行します。この時点で、
Math.java
すべての数学関数 ( を含む
Math.add
) を含むファイルがあり、次のように書いていると仮定しましょう。
MathTests.java
同じパッケージ内のファイル。次に、import ステートメントとクラス本体を設定しましょう: (JUnit インタビューの質問の可能性: JUnit テストをどこに配置するか、ソース ファイルをインポートする必要があるかどうかを尋ねられる場合があります。テスト クラスを同じパッケージに作成している場合は、メインクラスを使用している場合、テストクラスでソースファイルの import ステートメントは必要ありません。それ以外の場合は、ソースファイルをインポートしていることを確認してください。)
import org.junit.jupiter.Test; //gives us the @Test header
import static org.junit.jupiter.api.Assertions.assertEquals; //less typing :)
public class MathTests {
//...
}
最初の import ステートメントは
@Test
ヘッダーを提供します。すべてのテスト関数定義の先頭に' ' を直接書き込むこと
@Test
で、これが個別に実行できる単一の単体テストであることが JUnit に認識されます。後ほど、このヘッダーを使用して特定の単体テストを実行する方法を説明します。2 番目の import ステートメントにより、入力の手間が少し省けます。関数のテストに使用する主な JUnit 関数は to です
Assert.assertEquals()
。これは 2 つのパラメーター (実際の値と期待値) を受け取り、それらが等しいことを確認します。この 2 番目の import ステートメントを使用すると、
assertEquals(...
どのパッケージの一部であるかを毎回指定する必要がなく、「 」と入力するだけで済みます。ここで、2 + 2 が実際に 4 であることを検証するための非常に単純なテスト ケースを作成してみましょう。
import org.junit.jupiter.Test; // gives us the @Test header
import static org.junit.jupiter.api.Assertions.assertEquals; // less typing :)
public class MathTests {
@Test
public void add_twoPlusTwo_returnsFour(){
final int expected = 4;
final int actual = Math.add(2, 2);
assertEquals(“2+2 is 4”, actual, expected);
}
}
テスト関数の 5 行のそれぞれとその内容を見てみましょう。 5 行目: この
@Test
ヘッダーは、以下の関数定義が
add_twoPlusTwo_returnsFour()
実際に JUnit が個別に実行できるテスト関数であることを指定します。6 行目: これはテスト ケースの関数シグネチャです。テスト ケースは常に非常に特異です。2+2=4 などの 1 つの特定の例のみをテストします。テスト ケースには「 」の形式で名前を付けるのが慣例です
[function]_[params]_returns[expected]()
。ここで、
[function]
はテストしている関数の名前、
[params]
はテストしている特定のパラメーターの例、 は
[expected]
関数の期待される戻り値です。
void
関数全体の主な目的は実行することであるため、テスト関数はほとんどの場合、戻り値の型が ' ' になります。
assertEquals
、テストが成功したかどうかをコンソールに出力します。他のデータをどこにも返す必要はありません。
final
7 行目:の戻り値の型の' ' 変数を宣言し
Math.add (int)
、慣例に従って 'expected' という名前を付けます。その値は、私たちが期待する答えです (4)。
final
8 行目:の戻り値の型の' ' 変数を宣言し
Math.add (int)
、慣例により 'actual' という名前を付けます。その値は の結果です
Math.add(2, 2)
。9行目:ゴールデンライン。これは実際の値と予想された値を比較し、それらが等しい場合にのみテストに合格したことを示す行です。渡される最初のパラメータ「2+2 is 4」は、テスト関数の説明です。
関数が例外をスローした場合はどうすればよいでしょうか?
特定のテスト例で、実際の値と期待される値が等しいと主張する代わりに例外をスローする必要がある場合、JUnit にはヘッダーでこれを明確にする方法があります
@Test
。以下に例を見てみましょう。
Math.java
呼び出された関数があると仮定すると
Math.divide
、入力が 0 で除算できないことを確認する必要があります。代わりに、任意
Math.divide(a, 0)
の 'a' 値を呼び出そうとすると、例外 (
ArithmeticException.class
) がスローされます。ヘッダーで次のように指定します。
import org.junit.jupiter.Test; // gives us the @Test header
import static org.junit.jupiter.api.Assertions.assertEquals; // less typing :)
public class MathTests {
@Test (expectedExceptions = ArithmeticException.class)
public void divide_byZero_throwsException() throws ArithmeticException{
Math.divide(1, 0);
}
}
には複数の例外を含めることができます
expectedExceptions
が、次のように例外クラスをリストするには必ず括弧とカンマを使用してください。
expectedException = {FirstException.class, SecondException.class, … }
Java で JUnit テストを実行するにはどうすればよいですか?
JUnit を IntelliJ に追加する方法:
https://stackoverflow.com/questions/19330832/setting-up-junit-with-intellij-idea 通常テストを実行するのと同じようにプロジェクトを実行できます。テスト クラス内のすべてのテストを実行すると、アルファベット順に実行されます。JUnit 5 では、タグを追加することでテストに優先順位を追加できます
@Order
。例:
@TestMethodOrder(OrderAnnotation.class)
public class Tests {
…
@Test
@Order(2)
public void a_test() { … }
@Test
@Order (1)
public void b_test() { … }
…
}
アルファベット順およびコード内では
a_test()
が前に来ます が、順序では 1 が 2 よりも前にあるため、はここより前に実行されます。JUnit の基本についてはこれですべてです。ここで、JUnit の面接でよくある質問のいくつかに取り組み、その過程で JUnit についてさらに学びましょう。
b_test()
b_test()
a_test()
JUnit 面接の質問 (追加情報)
ここでは、JUnit の面接で最も人気のある質問を集めました。何か追加したいことがありましたら、下のコメント欄にお気軽にご記入ください。
Q:テストを自動的に失敗させるために、テスト メソッド内でどのメソッドを呼び出すことができますか? A: fail(「エラーの説明はここにあります!」); Q: Dog クラスをテストしています。Dog オブジェクトをテストするには、テストを実行する前にオブジェクトをインスタンス化する必要があります。そこで、setUp() 関数を作成して Dog をインスタンス化します。この関数は、すべてのテスト中に 1 回だけ実行する必要があります。テストを実行する前に JUnit が setUp() を実行できるようにするには、setUp() 関数シグネチャのすぐ上に何を置く必要がありますか? A: @BeforeClass (JUnit 5 の @BeforeAll) Q:上で説明した setUp() 関数の関数シグネチャは何でなければなりませんか? A: public static void。@BeforeClass (JUnit 5 では @BeforeAll) または @AfterClass (JUnit 5 では @AfterAll) を持つ関数はすべて静的である必要があります。 Q: Dog クラスのテストが完了しました。各テストの後にデータをクリーンアップし、コンソールに情報を出力する void teaDown() 関数を作成します。この関数は、すべてのテストの後に実行する必要があります。JUnitが各テストの実行後にtearDown()を実行できるようにするには、tearDown()関数シグネチャのすぐ上に何を置く必要がありますか? A: @After (JUnit 5 の @AfterEach)
GO TO FULL VERSION