かんがるーさんの日記

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

Spring Boot 2.0.x の Web アプリを 2.1.x へバージョンアップする ( その20 )( Spring Boot を 2.1.3 → 2.1.4 へバージョンアップする )

概要

記事一覧はこちらです。

Spring Boot 2.0.x の Web アプリを 2.1.x へバージョンアップする ( その19 )( dependency-management plugin を 1.0.6 → 1.0.7 へ、Checkstyle を 8.17 → 8.19 へ、PMD を 6.11.0 → 6.13.0 へバージョンアップする ) の続きです。

  • 今回の手順で確認できるのは以下の内容です。
    • Spring Boot を 2.1.3 → 2.1.4 へバージョンアップします。

参照したサイト・書籍

  1. Spring get ServletContext and provide it as a Bean
    https://stackoverflow.com/questions/47269496/spring-get-servletcontext-and-provide-it-as-a-bean

目次

  1. Spring Boot を 2.1.3 → 2.1.4 へバージョンアップする

手順

Spring Boot を 2.1.3 → 2.1.4 へバージョンアップする

build.gradle の以下の点を変更します。

buildscript {
    ext {
        group "ksbysample"
        version "2.1.4-RELEASE"
    }
    repositories {
        mavenCentral()
        maven { url "https://repo.spring.io/release/" }
        maven { url "https://plugins.gradle.org/m2/" }
    }
}

plugins {
    id "java"
    id "eclipse"
    id "idea"
    id "org.springframework.boot" version "2.1.4.RELEASE"
    id "io.spring.dependency-management" version "1.0.7.RELEASE"
    id "groovy"
    id "checkstyle"
    id "com.github.spotbugs" version "1.6.9"
    id "pmd"
    id "net.ltgt.errorprone" version "0.7.1"
    id "de.undercouch.download" version "3.4.3"
    id "com.gorylenko.gradle-git-properties" version "2.0.0"
}
  • buildscript block 内で version "2.1.3-RELEASE"version "2.1.4-RELEASE" に変更します。
  • plugins block 内で id "org.springframework.boot" version "2.1.3.RELEASE"id "org.springframework.boot" version "2.1.4.RELEASE" に変更します。

Gradle Tool Window の左上にある「Refresh all Gradle projects」ボタンをクリックして更新します。

clean タスク実行 → Rebuild Project 実行 → build タスクを実行してみるとテストが1件失敗しました。

f:id:ksby:20190407222622p:plain

失敗の原因を調べるために Project Tool Window で src/test/java/ksbysample でコンテキストメニューを表示して「Run 'Tests in 'ksbysample''」を選択すると java.lang.IllegalStateException: No WebApplicationContext found: no ContextLoaderListener registered? というエラーメッセージが表示されていました。

f:id:ksby:20190407223021p:plain

このエラーメッセージだけでは原因がまだ分からなかったので src/test/java/ksbysample/webapp/lending/web/LoginControllerTest.java の中の失敗しているテストを debug 実行してみたところ、下記の画像の赤枠の部分で IllegalStateException が throw されていました。

f:id:ksby:20190407224120p:plain

IllegalStateException が throw されるところも追ってみると、org.springframework.boot.actuate.autoconfigure.security.servlet.EndpointRequest#matches メソッドで WebApplicationContextUtils.getRequiredWebApplicationContext(request.getServletContext()); を呼び出していますが、

f:id:ksby:20190407224937p:plain

org.springframework.web.context.support.WebApplicationContextUtils#getRequiredWebApplicationContext メソッドでは WebApplicationContext が取得できないために IllegalStateException を throw していました。

f:id:ksby:20190407225153p:plain

つまり原因は、失敗しているテストでは MockServletContext のインスタンスを生成して SimpleRequestBuilder オブジェクトを生成してから org.springframework.test.web.servlet.MockMvc#perform メソッドを呼び出していますが、MockServletContext のインスタンスでは WebApplicationContext オブジェクトが取得できないため IllegalStateException が throw されてテストが失敗している、ということでした。

Mock ではない ServletContext を取得する方法を探してみると Spring get ServletContext and provide it as a Bean を見つけました。@Autowired すれば取得できるとのことなので src/test/java/ksbysample/webapp/lending/web/LoginControllerTest.java のテストを以下のように変更します。

    @Nested
    @SpringBootTest
    class 次回から自動的にログインするのテスト {

        @RegisterExtension
        @Autowired
        public TestDataExtension testDataExtension;

        @RegisterExtension
        @Autowired
        public SecurityMockMvcExtension mvc;

        @Autowired
        ServletContext servletContext;

        @Test
        void 次回から自動的にログインするをチェックすれば次はログインしていなくてもログイン後の画面にアクセスできる()
                throws Exception {
            // ログイン前にはログイン後の画面にアクセスできない
            mvc.noauth.perform(get(Constant.URL_AFTER_LOGIN_FOR_ROLE_ADMIN))
                    .andExpect(status().isFound())
                    .andExpect(redirectedUrl("http://localhost/"))
                    .andExpect(unauthenticated());

            // 「次回から自動的にログインする」をチェックしてログインし、remember-me Cookie を生成する
            org.springframework.mock.web.MockHttpServletRequest request
                    = formLogin()
                    .user("id", mvc.MAILADDR_TANAKA_TARO)
                    .password("password", "taro")
                    .buildRequest(servletContext);
            request.addParameter("remember-me", "true");
            SimpleRequestBuilder simpleRequestBuilder = new SimpleRequestBuilder(request);
            MvcResult result = mvc.noauth.perform(simpleRequestBuilder)
                    .andExpect(status().isFound())
                    .andExpect(redirectedUrl(Constant.URL_AFTER_LOGIN_FOR_ROLE_ADMIN))
                    .andExpect(authenticated().withUsername(mvc.MAILADDR_TANAKA_TARO))
                    .andReturn();
            Cookie[] cookie = result.getResponse().getCookies();

            ..........
  • @Autowired ServletContext servletContext; を追加します。
  • // 「次回から自動的にログインする」をチェックしてログインし、remember-me Cookie を生成する のコメントの下に記述していた MockServletContext servletContext = new MockServletContext(); を削除します。

再度 clean タスク実行 → Rebuild Project 実行 → build タスクを実行してみると今度は "BUILD SUCCESSFUL" のメッセージが出力されました。

f:id:ksby:20190407235520p:plain

履歴

2019/04/08
初版発行。