こんにちは。ステックアップアカデミー講師のかびらです。現役のITエンジニアであり、かつAFP保持者として、このチャンネルを通して、ITエンジニアとフリーランスに必要な、ITとお金に関する情報を配信しています。
今回は「誰もが一度はつまずく、Spring Bootを解説」シリーズの第6回目です。このシリーズでは、私も含め、誰もが共通してつまずきやすいポイントを、Javaのスキルや経験が浅い、若手エンジニアでも簡単に理解できるよう、極力難しい用語を使わずに解説しています。
今回から新たにテスト編をスタートします。今回は、テストに取り組む前に、テストの仕組みの部分で最初からつまずきそうなポイントを絞ってわかりやすく解説します。
テストクラスの命名規則
Spring Bootの機能に直接関連はしないものの、テストを始める前に知っておくべき重要な事項についてお話しします。 この情報は、後々面倒になりそうな問題を未然に防ぐために役立ちます。
テストクラスの命名について、迷うことはありませんか? 多くのプロジェクトでは命名規則がプロジェクトの規約として定められているため、大きな問題にはならないかもしれません。しかし、テストを開始する前に明確な命名規則を設定しておくことは非常に重要です。
では、テストコードの適切な名前は何でしょうか? 私のおすすめは、上のイメージのようにテスト対象の「本番コードのクラス名」に「Test」を加える方法です。例えば、本番コードのクラス名が「PartA」であれば、テストコードの名前は「PartATest」となります。この命名規則をおすすめする理由は主に2つあります。
「本番コードのクラス名に「Test」を付けたテストコードのクラス名」がおすすめの理由、1つ目は「対象のテストのわかりやすさ」、2つ目は「Mavenでのテスト実行時の挙動」です。順番に解説します。
まず1つ目は、何と言っても「対象のテストのわかりやすさ」でしょう。 本番コードのクラス名に基づいてテストコードのクラス名を設定することで、そのテストがどの本番コードを対象としているのかが一目でわかります。この直接的なリンクにより、テストコードの目的が明瞭になります。
さらに、本番コードとテストコードでパッケージ名を一致させることも大切です。これにより、コードの整理整頓が容易になり、関連するコード間の照合がスムーズに行えます。
また、テストコード内のメソッド名も、本番コードに対応させることをおすすめします。これは、本番コードとテストコード間の関係性をさらに鮮明にし、コードの読みやすさを向上させます。
このように、本番コードとテストコードのクラス名やパッケージ、メソッド名を統一することで、テストの目的と内容がより明確になります。
2つ目は、「Mavenにおけるテスト実行の挙動」です。 Mavenはビルドツールですが、今回はその詳細には触れません。今回注目しているのは、Mavenがテストを実行する際に特定の命名規則に従う必要があるということです。
このMavenの仕様では、ある特定の命名パターンに従っていないと、クラスはテストクラスとして認識されず、その結果テストが実行されません。Mavenはデフォルトで、下記に示す特定のクラス名をテストクラスとして認識します。
- **/Test*.java
- **/*Test.java
- **/*Tests.java
- **/*TestCase.java
一般的には、開発中にはSTSのJunitテスト機能を使ってテストが実行されることが多いです。Junitではこのような特定の命名規則に従う必要はなく、すべてのテストコードが実行されます。しかし、この違いに気付かず、Mavenでのビルド時にテストが実行されないという事態に直面することがあります。実際、私自身も過去にこのようなトラブルに遭遇したことがあります。
そのため、Mavenでのビルドをスムーズに進めるためにも、テストコードを実装する前に、この特定の命名規則を頭に入れておくことが重要です。私のおすすめは、指定された4つの命名規則の中から、「Test」を語尾に付けるルールを採用しています。
テスト用のプロパティファイル
前回は、異なる環境に応じてプロパティファイルを変更する方法について説明しました。 今回は、テスト環境のためにこれらのプロパティファイルを書き換える方法をお伝えします。
本番コードの開発が完了し、テストフェーズに入る際、よくあるのがプロパティファイル内の値を本番環境とは異なるものに設定したいというケースです。このような場合、どのように対処すれば良いのでしょうか?
実は、この問題は比較的簡単に解決できます。テスト用にプロパティファイルの値を変更したい場合、テストのリソースディレクトリ (test/resources) 下に本番コード用のプロパティファイルと同じ名前のファイルを配置するだけです。この方法を使えば、テスト実行時にはこの新しいファイルの値が利用されます。
上にそのイメージを掲載しています。まず、本番コードが配置されている main/resources ディレクトリには、本番用のプロパティファイル「application.yml」が置かれています。次に、テスト用のプロパティファイルを書き換えるために、test/resources ディレクトリ下にも同名の「application.yml」を配置します。
具体的な内容の違いを見てみましょう。本番用の「application.yml」では、「value1」の値が英語で「Test1」と設定されています。一方で、テスト用の「application.yml」では、同じ「value1」がカタカナで「テスト1」と設定されています。
このようにプロパティファイルを設定した状態でテストを実施すると、テスト環境では「テスト1」の設定が適用されます。
テスト環境でプロパティファイルの値を変更する方法についてこれまで解説してきましたが、この手法は結局のところSpring Bootに不慣れな方にとって理解しにくい部分があります。 実際、私自身も最初は理解に苦しみました。
そのため、テスト用のプロパティファイルを作成する際には、より直感的に理解しやすい方法をおすすめします。具体的には、「test」という環境識別子を付けたプロパティファイルを作成する方法です。例えば、test/resources ディレクトリ下に「application-test.yml」というファイル名で配置します。
テストコードでは、クラスに「@ActiveProfiles(“test”)」というアノテーションを付けることで、「application-test.yml」を指定します。これにより、テスト実行時には「application-test.yml」の設定が読み込まれ、本番環境とは異なるプロパティ値でテストを行うことが可能になります。
Junitのバージョン
この章では、私がITエンジニアとして経験した中でも特につまずいた内容です。Junitのバージョンの違い。 実は、この点で私自身もかなりの苦労を経験しました。
現在、Junitの最新バージョンは5です。そのため、これからSpring Bootの開発を始める方々は、Junit5に関する情報だけを押さえておけば十分です。しかし、実際の現場では、古くからSpring Bootを使用しており、Junit4を使ったテストコードが残っているケースが多いです。私が関わったプロジェクトもその一つでした。
Junit4とJunit5が混在する状況では、テストが上手く動かないことが頻繁にあります。現場では、他のコードを流用したり、インターネットからコードを引用したりすることが一般的ですが、その際にJunitのバージョンに関する知識が不足していると問題が発生しやすいです。特に困るのは、Junit4で書かれたテストとJunit5で書かれたテストの区別がつきにくいことです。
この章では、Junit4とJunit5の違いとそれらを見分ける方法について詳しく解説します。
さて、ここで皆さんにちょっとしたクイズを出しましょう。 上のイメージには「PartATest1」と「PartATest2」という2つのテストクラスが表示されています。これらのテストクラスのうち、一つはJUnit4で、もう一つはJUnit5で書かれています。この2つのクラスの中で、どちらがJUnit5を使用して書かれたテストクラスでしょうか? 少し考えてみてください。
どうでしょうか?答えは出ましたか?正解は、右側に表示されているテストクラスが、実はJUnit5で書かれていました。 どう思われますか?このクイズは少し意地悪でしたね。示された両方のテストコードには、一見して同じコードが書かれており、見た目だけではJUnit4とJUnit5を区別することはできません。
それでは、どうやってこれらのJUnitのバージョンを見分けることができるのでしょうか? そのヒントは、Import文に隠されています。今はImport文の一部が非表示になっているため、全体を表示してみましょう。
インポート文を全て表示すると違いがわかります。注目すべきは、@TestアノテーションのImport文です。 左側のテストコードではImportが「junit.Test」となっていますが、右側のテストコードでは「junit.jupiter.api.Test」となっていることがわかります。
この違いが、テストクラスがJUnit4なのかJUnit5なのかを判別するカギとなります。JUnit4の場合は「junit.Test」、JUnit5の場合は「jupiter」が含まれる「junit.jupiter.api.Test」を使用します。この違いは一見して判別が難しいですね。特に、STSを使用している場合、テストクラスを開くとImport文がデフォルトで部分的に非表示になっているため、このような混乱を招くことがあります。
このトラップにはまると、Junitのバージョンに関する知識がないと、テストコードがどのバージョンのJunitで動作しているかを知ることなく作業を進めてしまうことになります。ただし、どちらのテストコードも実際には正常に動作し、テストに成功します。テストが成功するのであれば大した問題にならないと思われるかもしれませんが、ややこしいのはここからです。
今度はテスト実行前に事前処理を行うために、@Beforeアノテーションを使用したsetupメソッドを実装してみました。 このsetupメソッドでは、テストの期待値をあらかじめ設定しています。ただし、これは検証目的での記述であり、実際の開発現場ではこのような方法で期待値を事前に設定することは避けるべきです。
さて、この@Beforeアノテーション付きのsetupメソッドを、JUnit4とJUnit5で同様のコードを用いて実装しました。このテストをそれぞれ実行すると、どのような結果になるでしょうか? 実は、JUnit4ではテストが成功するのに対し、JUnit5ではテストが失敗します。なぜこのような違いが生じるのでしょうか?
この疑問に答えるために、次にJUnit5でのテスト実行時のログを確認してみましょう。
JUnit5でテストコードを実行し、失敗した際のログ(上のイメージ)を見てみましょう。 ログを確認すると、本番コードの結果が「Hello PartsA」という文字列を返しているにもかかわらず、期待値が空となっていることがわかります。これを詳しく解析すると、@Beforeアノテーションが付いたsetupメソッドが実行されていないことが原因です。
では、なぜJUnit5のテストコードでsetupメソッドが動作していないのでしょうか? 実は、JUnit5では、事前処理の書き方がJUnit4とは異なっているためです。JUnit5での事前処理の書き方は、JUnit4のものとは記法が異なります。
では、JUnit5での正しい記法について見ていきましょう。 JUnit5では、事前処理には@BeforeEachアノテーションを使用します。これは、JUnit4での@Beforeアノテーションとは異なる点です。JUnit5では、@BeforeEachアノテーションの使用が必要となります。
@BeforeEachアノテーションを適用することで、JUnit5のテストコードも正常に動作します。このように、JUnit4とJUnit5では、アノテーションの記法が異なります。この違いを理解しておかないと、JUnit5で動作するテストコードにJUnit4の@Beforeアノテーションを誤って使用してしまったり、その逆もあり得ます。
このような誤りがあると、テストコードが動かない原因の解析に時間を費やすことになりかねません。そのため、どのJUnitバージョンを使用しているのかをImport文で確認すること、そしてそのバージョンに合ったアノテーションの記法を適用することが非常に重要です。
ここまで、@Beforeアノテーションを例に挙げてJUnit4とJUnit5の違いを説明してきました。しかし、これらのバージョン間で変更されたアノテーションは他にも多数あります。 その中のいくつかの代表例をご紹介します。
まず、すでに触れた@Beforeアノテーション(JUnit4)と@BeforeEachアノテーション(JUnit5)の違いです。これらは、各テストメソッドが実行される前に必要な事前処理を行うために使います。
次に、JUnit4の@BeforeClassアノテーションが、JUnit5では@BeforeAllアノテーションに変わりました。これらはテストクラス内で一度だけ呼び出され、全テストメソッドの実行前に必要な事前処理を行う際に使用します。
また、JUnit4の@Afterアノテーションが、JUnit5では@AfterEachアノテーションに置き換わり、各テストメソッドの事後処理に使用されます。
最後に、JUnit4の@AfterClassアノテーションが、JUnit5では@AfterAllアノテーションに変更され、テストクラス内の全テストメソッドが実行された後の事後処理に用いられます。
JUnit4とJUnit5で変わったアノテーションはまだ他にもありますが、これらについては次回以降で解説します。
さいごに
今回はここまでです。今回は、Spring Bootで作ったアプリをテストしようとする際に、そもそも序盤でつまずきやすいポイントを解説してきました。次回は、Spring Bootで実際にテストする方法について解説したいと思います。今回もまた一つ賢くなりましたね!次回の記事もぜひご覧ください。お読みいただき、ありがとうございました!