初めてのユニットテスト -Junit-
はじめまして、2023年度に新卒入社した塩山と申します。今回のテーマとして取り上げるのは、私が実務を任されてから一番苦戦してきたJunitを用いたテストコードの実装です。テストコードは効率よくプロダクトの品質を担保するために必要不可欠です。業務でテストを行う上で抑えておきたい基礎を一通りまとめました。初めてテストコードを触る方やもう一度勉強し直したい方は参考にしていただけたら幸いです。
目次
Junitとは
テストの作成
Junitの基本事項
- 事前準備
- テストメソッドの記述ルール
- テストコードのおおまかな流れ
- 結果確認
アサーション
- assertEquals
- assertThrows
- fail
- その他のアサーション
知っておくと便利な機能
- 構造化(@Nested)
- フィルタリング(@Tag)
- 暗黙的セットアップ(@BeforeEach)
- パラメータ化テスト(@ParameterizedTest)とカンマ区切りデータ(@CsvSource)
最後に
Junitとは
Junitは、Javaプログラムのユニットテストを行うためのフレームワークです。
ユニットテストとは、メソッドやクラスといったプログラムの個々の部分が期待通りに動作するかどうかを確認するためのテストです。プロジェクトの品質を向上させ、バグを早期に発見するためにユニットテストを導入する上でJUnitは非常に有用です。
※今回は現時点で最新かつ最も使われているJUnit5を使用していきます。
テストの作成
-
プロジェクトの中にsrc/mainに対してsrc/testフォルダがあることを確認します。(なければ、プロジェクトを右クリック > New > Source Folder > Folder nameにsrc/testを入力し、フォルダを作成 )
-
mainとtest内に同じパッケージがあることを確認します。(なければ、testフォルダを右クリック > New > Other > Package > Nameにパッケージ名(今回はjunit.sample)を入力)
-
src/main/junit/sampleの中にあるテストしたいプロダクトコード(今回はCalculator.java)を右クリック > New > Other
-
> junit > JUnit Test Caseを開きます。
5.以下のように入力して、テストクラスを作成します。
※Source Folderが(プロジェクト名)/src/testになっていることや、Packageがmainのものと同じになっていること、Nameがプロダクトコードのクラス名の後にTestがついている(今回はCalculatorTestとなっている)ことを確認します。
6.以下のようなテストクラスが作られていることを確認します。
Junitの基本事項
- 事前準備
JunitはEclipseにデフォルトで組み込まれているため、pom.xmlやbuild.gradleといった設定ファイルに記述する必要はありません。
import文を書けば使えるようになります。今回は一般的によく用いられるorg.junit.jupiter.apiのモジュールを使用します。
- テストメソッドの記述ルール
-
-
@Test アノテーションを付ける
-
戻り値の型をvoidとする
-
テストメソッド名は"test"を先頭に付けるとわかりやすい
-
テストメソッドの中にテストコードを書く
-
-
@Test |
- テストコードのおおまかな流れ
-
-
テスト対象のオブジェクト生成
-
期待値の定義
-
メソッドを呼び出して実測値を出す
-
期待値と実測値が等しいか確認
-
[例] 今回の例では、Calculator.javaのaddメソッドをテストします。
[Calculator.java]package junit.sample; |
[CalculatorTest.java] @Test |
- 結果確認
動かしたいテストメソッド(今回はtestAdd)を右クリック > Debug as > Junit test を選択すると、下図のようなJunitのウィンドウが表示されます。下図のような緑のバーが出ていたら、成功となります。失敗する場合はErrorsかFailuresが出ます。Errorsはテストが正常に実行されなかったことを表します。コードにExceptionやErrorがあるときに生じます。一方で、Failuresは正常にテストは実行されたものの、期待通りのテスト結果が得られなかったときに出ます。
アサーション
アサーションとはJunitで値の比較検証を行うことを指します。 今回は一般的に使われることの多いorg.junit.jupiter.api.Assertionsクラスのアサーションメソッドをいくつか紹介します。
- assertEquals
期待値と実測値が等しいときにテストが成功
※テストコードのおおまかな流れの節で例あり
- assertThrows
テスト対象の処理を実行した際に、指定した例外が発生したらテストが成功 [例] 今回の例では、Calculator.javaのdivメソッドの引数yが0だった時に
IllegalArgumentExceptionが発生するかをテストします。
[Calculator.java] |
[CalculatorTest.java] @Test |
- fail
テストを失敗させるアサーションメソッド言い換えると、fail()が実行される分岐だとテストは失敗
assertThrowsと同様に例外をテストしたときに使われます。assertThrowsは例外が具体的に分かっているときに
使われるのに対して、failは分からないときに使われます。
[例] 今回の例では、Calculator.javaのdivメソッドの引数yが0だった時にtry内のdivの後の
処理が実行されないことをテストします。
[CalculatorTest.java] @Test |
- その他のアサーション
-
-
assertTrue: 実測値がTrueの場合にテスト成功
-
assertNotNull: 実測値がNullではない場合にテスト成功
-
assertAll: 複数のテストを実行でき、1つのテストに失敗しても残りのテストが実行される
-
-
知っておくと便利な機能
- 構造化(@Nested)
役割やプロダクトコードのメソッドごとにテストメソッドをグループ化することができます。
これを用いると、テストケースが増加した際でも可読性が担保できます。
また、プロダクトコードのメソッドごとにテストを実行することも可能です。
[例]CalculatorのDivメソッドに対してのテストメソッド3つをグループ化する
@Nested @Test @Test |
上記のコードのようにまとめたいテストメソッドを@Nestedアノテーションを付けたclassとして{}で囲みます。
そうすることでテストを実行した際に、下図のように入れ子構造になり可読性が上がります。
また、ネストしたclassだけをテスト実行することも可能です。
- フィルタリング(@Tag)
@Nestedと役割は少し似ていて、グループ化したい時に付けます。これを用いると、テストを実行するときにフィルタリングをすることができます。
[例] 2つのExceptionに関するテストメソッドにタグ付けをする
@Test @Tag("Exception") @Test |
上記のコードのようにタグ付けしたいテストメソッドの前に@Tagを付ければ良いです。
実行するには、右クリック > Debug Configurations > Testタブ > Include and exclude tags> configureを押して、Include Tagsに実行したいタグを、exclude Tagsには実行したくないタブを付けます。今回はInclude TagsにExceptionを入力してみます。入力できたら、OKを押して、Debugを押します。
Debugしてみると、Exceptionタグを付けたテストメソッドだけ実行されていることを確認できます。
- 暗黙的セットアップ(@BeforeEach)
これを用いると、各テストメソッドで共通して行う処理をまとめて書くことができ、処理の高速化ができます。
[例]全てのクラスメソッドで行われていたCalculatorのオブジェクト生成をまとめる
(変更前)
package junit.sample; @Test @Nested @Test @Test @Tag("Exception") @Test @Tag("Exception") |
(変更後)
package junit.sample; @Test void testAdd() { @Nested @Test @Test @Tag("Exception") @Test @Tag("Exception") |
- パラメータ化テスト(@ParameterizedTest)とカンマ区切りデータ(@CsvSource)
@ParameterziedTestを用いると、テストメソッドの引数にデータを渡せるようになります。@CsvSourceを用いると、カンマ区切りのデータを定義できます。 これらを組み合わせると、引数により条件が増えた場合でもテストメソッドの数を抑えることができます。
[例] 「20歳以上」かつ「東京都在住」かつ「利用回数が1回以上」の場合に優待を受けられるプロダクトコードのテスト
[Customer.java] |
[CustomerTest.java] @ParameterizedTest @CsvSource({ "20,'東京都',1,true", public void testCheckBonus(int age, String address, int count, boolean expected) { |
CSVデータ1行に対してテストが1回実行され、データが前から順番にテストメソッドの引数に入っていきます。
このように4回分のテストを1つのテストメソッドで実行できるようになります。
テストケースが多い場合はこのパラメータ化テストを使うと効率よくテストできるようになると思います。
最後に
ブログをご覧いただきありがとうございました。今回はJunitの基礎や便利な機能についてまとめてみました。テストコードは効率よくプロダクトの品質を保つために必要不可欠です。今後もJunitについて調査を進め、使いこなせるようにしたいと思います。次回は、今回分量の問題で書ききれなかったmockitoのspyやmockについてまとめたいと思います!