読者です 読者をやめる 読者になる 読者になる

かんがるーさんの日記

最近自分が興味をもったものを調べた時の手順等を書いています。今は Spring Boot をいじっています。

Spring Boot 1.3.x の Web アプリを 1.4.x へバージョンアップする ( その15 )( テストクラスのアノテーションを 1.4 のものに変更する )

概要

記事一覧はこちらです。

Spring Boot 1.3.x の Web アプリを 1.4.x へバージョンアップする ( その14 )( spring-boot-gradle-plugin は dependency-management-plugin を自動的に適用するので build.gradle に記述する必要がありませんでした ) の続きです。

  • 今回の手順で確認できるのは以下の内容です。

参照したサイト・書籍

  1. Spring Boot 1.4.0 Release Notes - Test utilities and classes
    https://github.com/spring-projects/spring-boot/wiki/Spring-Boot-1.4-Release-Notes#test-utilities-and-classes

  2. Spring Boot Reference Guide - 40. Testing
    http://docs.spring.io/spring-boot/docs/1.4.5.RELEASE/reference/htmlsingle/#boot-features-testing

目次

  1. どう書き換えればよいのか?
  2. 変更する方針を決める
  3. 全てのテストを @RunWith(SpringRunner.class) + @SpringBootTest に変更してみる
  4. Controller, RestController 以外のテストを @SpringBootTest(webEnvironment = SpringBootTest.WebEnvironment.NONE) に変更してみる
  5. 次回は。。。

手順

どう書き換えればよいのか?

最低限の変更だけ行うのであれば、以下のように書き換えれば動きます。

@RunWith(SpringJUnit4ClassRunner.class)
@SpringBootTest(classes = Application.class)
@WebAppConfiguration
public class LendingapprovalFormValidatorTest {

↓↓↓

@RunWith(SpringRunner.class)
@SpringBootTest
public class LendingapprovalFormValidatorTest {
  • @RunWith(SpringJUnit4ClassRunner.class)@RunWith(SpringRunner.class) へ変更します。
  • @SpringBootTest(classes = Application.class), @WebAppConfiguration@SpringBootTest へ変更します。

@RunWith(SpringRunner.class) の方は単純な書き換えなので迷わないのですが、@SpringBootTest の方は webEnvironment 属性で NONE, MOCK, RANDOM_PORT, DEFINED_PORT のどれを指定すればよいのかが最初よく分かりませんでした。結論としては @SpringBootTest だけ書けば問題なくテストは動きます(この時はデフォルト値である MOCK が指定された状態です)。

1.3 のアノテーションの時と MOCK, RANDOM_PORT, DEFINED_PORT それぞれを指定した時のテストの実行速度がどれくらい違うのか気になったので src/test/java/ksbysample/webapp/lending/web/lendingapproval/LendingapprovalControllerTest.java で試してみます。NONE はエラーになるので最初から除外します。

  • テストは LendingapprovalControllerTest.java のクラス名の左側のアイコンをクリックして表示される「Run ‘LendingapprovalControllerTest'」を選択して実行します。

    f:id:ksby:20170326090354p:plain

  • 計測時間は IntelliJ IDEA の以下の場所に表示されるものを使用します。

    f:id:ksby:20170326090707p:plain

1回目 2回目 3回目 4回目 5回目 平均
1.3 5s 791ms 6s 128ms 5s 720ms 5s 609ms 6s 56ms 5s 961ms
MOCK 5s 906ms 6s 43ms 5s 979ms 6s 225ms 5s 910ms 6s 9ms
RANDOM_PORT 5s 657ms 5s 766ms 5s 555ms 5s 207ms 5s 388ms 5s 514ms
DEFINED_PORT 6s 702ms 5s 542ms 5s 694ms 5s 632ms 5s 787ms 5s 871ms

MOCK で遅いの?と思ったので、この後3回程試してみたところ 5s 625ms、6s 8ms、5s 274ms でした。最後の1回だけはそこそこ速いです。MOCK, RANDOM_PORT, DEFINED_PORT でたいした差はないのではないか?という気がしますが、今ひとつはっきりしませんね。。。

もう1つ思ったのは、Controller のテストでなければ @SpringBootTest ではなく @SpringBootTest(webEnvironment = SpringBootTest.WebEnvironment.NONE) を指定した方がテストが速いのではないか?という点です。

src/test/java/ksbysample/webapp/lending/web/lendingapproval/LendingapprovalFormValidatorTest.java で試してみます。

1回目 2回目 3回目 4回目 5回目 平均
1.3 215ms 167ms 254ms 207ms 180ms 204ms
NONE 190ms 291ms 180ms 182ms 199ms 208ms
MOCK 208ms 197ms 201ms 211ms 196ms 202ms

NONE は2回目が遅かった影響で平均が他と大差なくなっていますが、MOCK と比較すると 10~20ms 程度速くなるような気がします。

変更する方針を決める

まずはシンプルに一律以下のアノテーションに変更してテストが全て成功することを確認してみます。MOCK, RANDOM_PORT, DEFINED_PORT で差がない気がするので、シンプルに @SpringBootTest だけ書くことにします。

@RunWith(SpringRunner.class)
@SpringBootTest
public class LendingapprovalFormValidatorTest {

その後で Controller, RestController 以外のテストを @SpringBootTest(webEnvironment = SpringBootTest.WebEnvironment.NONE) に変えて、テストの総実行時間が速くなるのか確認してみたいと思います。

また、Controller, RestController のテストを Controller の機能だけのテストにしていれば @WebMvcTest アノテーションを使用するのもありだな、と思いましたが、作成してあるテストは Service まで含めたものになっているので、今回 @WebMvcTest は使いません。Controller は Mock を利用して Controller だけのテストをした方がよいのかもしれません。

全てのテストを @RunWith(SpringRunner.class) + @SpringBootTest に変更してみる

以下の手順でアノテーションを変更します。

  • Ctrl+Alt+F で「Find in Path」ダイアログを表示した後、@RunWith(SpringJUnit4ClassRunner.class) で project 全体を検索します。
  • ヒットした箇所を @RunWith(SpringRunner.class) + @SpringBootTest の組み合わせに変更した後、Ctrl+Alt+o を押して不要な import 文を削除します。

また Spock で書いてあるテストについては以下のように変更します。

@ContextConfiguration(loader = SpringApplicationContextLoader.class, classes = Application.class)
@WebAppConfiguration
class ValuesHelperTest extends Specification {

↓↓↓

@SpringBootTest
class ValuesHelperTest extends Specification {

全てのテストを変更したら clean タスク → Rebuild Project → build タスクを実行してみます。

build タスクは “BUILD SUCCESSFUL” が表示されて成功しました。

f:id:ksby:20170326201050p:plain

Project Tool Window の src/test から「Run ‘All Tests’ with Coverage」を実行してみます。

こちらも全てのテストが成功しました。テストの実行速度も体感的には以前と変わりません、というより少し速くなっているような気がします。

f:id:ksby:20170326202958p:plain

5回計測してみます。

1回目 2回目 3回目 4回目 5回目 平均
29s 709ms 29s 431ms 28s 175ms 29s 293ms 29s 344ms 29s 190ms

IntelliJ IDEA を 2016.3.4 → 2016.3.5 へ、Git for Windows を 2.11.1 → 2.12.0 へバージョンアップ の記事だと 1m 3s 669ms、Spring Boot 1.3.x の Web アプリを 1.4.x へバージョンアップする ( その13 )( RestTemplate で WebAPI を呼び出している処理に spring-retry でリトライ処理を入れる ) の記事だと 49s 111ms でしたので、やはり速くなっているようです。

Controller, RestController 以外のテストを @SpringBootTest(webEnvironment = SpringBootTest.WebEnvironment.NONE) に変更してみる

以下の手順でアノテーションを変更します。

  • Ctrl+Alt+F で「Find in Path」ダイアログを表示した後、@SpringBootTest で project 全体を検索します。
  • ヒットした箇所のうち、Controller, RestController のテスト以外を @SpringBootTest(webEnvironment = SpringBootTest.WebEnvironment.NONE) に変更します。

全てのテストを変更したら clean タスク → Rebuild Project → build タスクを実行してみます。

build タスクは “BUILD SUCCESSFUL” が表示されて成功しました。

f:id:ksby:20170326212455p:plain

Project Tool Window の src/test から「Run ‘All Tests’ with Coverage」を実行してみると、こちらも全てのテストが成功しました。

f:id:ksby:20170326212821p:plain

5回計測してみます。

1回目 2回目 3回目 4回目 5回目 平均
29s 314ms 31s 566ms 30s 978ms 30s 473ms 29s 504ms 30s 367ms

少しですが遅くなるようです。MOCK と NONE が混在しているからでしょうか。。。? こちらの変更は元に戻して @SpringBootTest のみにします。

最初テストクラスのアノテーションはなんか複雑になったような気がしていましたが、@RunWith(SpringRunner.class) + @SpringBootTest を書けば十分でした。実はシンプルになったんですね。

次回は。。。

モックは JMockit で作成していますが、Spring Boot 1.4 から @MockBean アノテーションが提供されて Mockito と組み合わせてモックを作ることが推奨されているようなので、@MockBean + Mockito で作り直してみます。

ソースコード

履歴

2017/03/27
初版発行。