かんがるーさんの日記

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

Spring Boot でログイン画面 + 一覧画面 + 登録画面の Webアプリケーションを作る ( 番外編 )( Spring Boot を 1.2.1 → 1.2.2 へバージョンアップ )

概要

Spring Boot が 1.2.2 にバージョンアップしていたので、バージョンアップします。

ソフトウェア一覧

参考にしたサイト

  1. 虎塚 - メモ: JUnitのexpectedフィールドとRuleアノテーションで例外のテスト
    http://d.hatena.ne.jp/torazuka/20121002/junit

手順

build.gradle の変更、反映

  1. 画面左側の Project View から build.gradle をダブルクリックして開きます。

  2. build.gradle をリンク先のその1の内容に変更します。

  3. 画面右側の Tool Window から「Gradle」をクリックして Gradle tasks View を表示します。View 表示後、View の左上にある「Refresh Gradle projects」アイコンをクリックして、変更した build.gradle の内容を反映します。

    • 更新にはしばらく時間がかかります。画面右下に進捗状況が表示されますので、進捗状況の表示が消えるまで待ちます。
  4. Project View の External Libraries に表示される org.springframework.boot:spring-boot のバージョンが 1.2.2.RELEASE になっていることを確認します。

    f:id:ksby:20150303070044p:plain

テスト実行、確認

  1. Project View の一番上の階層の ksbysample-webapp-basic を選択した後コンテキストメニューを表示し、「Run 'Tests in 'ksbysample...' with Coverage」を選択してテストを実行しますが、なぜか全てのテストが失敗しました。。。

  2. ログを見てみると、java.lang.IllegalAccessError: tried to access class org.springframework.test.context.support.TestPropertySourceUtils from class org.springframework.boot.test.SpringApplicationContextLoader$PropertySourceLocationsInitializer というメッセージが出力されていました。

  3. 何のエラーなのか全然検討がつかないので、ログに出力されているソースファイルの該当箇所を見てみることにします。ソースファイルがリンクになっているのでクリックして開きます。

    f:id:ksby:20150303220510p:plain

  4. SpringApplicationContextLoader.java が開きます。TestPropertySourceUtils の部分が赤く表示されています。該当のクラスがないようです。

    f:id:ksby:20150303220850p:plain

  5. ソースファイルの上部に移動し、どのパッケージに入っているクラスなのか確認します。org.springframework.test.context.support パッケージにあるはずのクラスのようです。org.springframework.test... とあるのでライブラリも test の文字列が含まれるものと思われます。

    f:id:ksby:20150303221131p:plain

  6. 画面左側の Project View の External Libraries を見てみると org.springframework.boot:spring-boot-starter-test:1.2.0.RELEASEorg.springframework:spring-test:4.1.3.RELEASE というライブラリがありました。

    f:id:ksby:20150303222129p:plain

    バージョンアップ開始前に Spring Boot の GitHub で 1.2.2 のバージョンアップの内容を見ていたのですが、「Upgrade to Spring 4.1.5.RELEASE」というものがあり、今回 Spring Framework 本体のバージョンが 4.1.5 に上がるんだなと思っていましたので、4.1.3.RELEASE になっていることに違和感を感じます。

    また spring-boot-starter-test のバージョンが 1.2.2.RELEASE になっていないことも気になります。spring-boot-starter-... で始まるライブラリは他にも 1.2.0.RELEASE のものがありました。

  7. build.gradle に記述している test 関連のライブラリは testCompile("org.springframework.boot:spring-boot-starter-test") ですので、spring-boot-starter-test の jCenter に登録されている最新バージョンが何か確認してみます。

    bintray / jcenter / org.springframework.boot:spring-boot-starter-test
    https://bintray.com/bintray/jcenter/org.springframework.boot%3Aspring-boot-starter-test/view

    上記のページを見てみると 1.2.2.RELEASE が最新でした。やはり本体のバージョンアップに合わせて、他の spring-boot-starter のライブラリもバージョンが上がると考えてよいような気がします。

  8. build.gradle を編集して spring-boot-starter-test の 1.2.2.RELEASE が使用されるようにしてみます。また他にも spring-boot-starter-... で始まるライブラリで 1.2.2.RELEASE になっていないものがありましたので、spring-boot-starter-... で始まるライブラリは全て 1.2.2.RELEASE が使用されるようにしてみます。

  9. build.gradle をリンク先のその2の内容に変更します。

  10. 画面右側の Tool Window から「Gradle」をクリックして Gradle tasks View を表示します。View 表示後、View の左上にある「Refresh Gradle projects」アイコンをクリックして、変更した build.gradle の内容を反映します。

    f:id:ksby:20150304020159p:plain

    今度は spring-boot-starter-... で始まるライブラリは全て 1.2.2.RELEASE となり、spring-... で始まるライブラリ ( spring-data 系や spring-security 系は除く ) も 4.1.5.RELEASE になりました。

  11. 再度 Project View の一番上の階層の ksbysample-webapp-basic を選択した後コンテキストメニューを表示し、「Run 'Tests in 'ksbysample...' with Coverage」を選択してテストを実行します。今度は @Ignore アノテーションを付加しているテストを除き成功しました。

    f:id:ksby:20150303231403p:plain

  12. src/test/java/ksbysample/webapp/basic の下の ApplicationTest.java のテストのコメントアウトを外していなかったので、外して再度テストします。

  13. Project View の一番上の階層の ksbysample-webapp-basic を選択した後コンテキストメニューを表示し、「Run 'Tests in 'ksbysample...' with Coverage」を選択してテストを実行しますが、再びテストが失敗するようになりました。

    f:id:ksby:20150304003644p:plain

  14. ログを見ると java.lang.AssertionError: Expected exception: java.lang.Exception というメッセージが出力されていました。調査した結果、テストクラスのメソッド@Test(expected = Exception.class) と書いていましたが、expected 属性とはそのテストで発生する例外クラスを指定するものでした。testApplicationProductMode, testApplicationDevelopMode では例外は発生しないので、ただの @Test アノテーションにします。

  15. src/test/java/ksbysample/webapp/basic の下の ApplicationTest.javaリンク先のその1の内容に変更します。

  16. 再度 Project View の一番上の階層の ksbysample-webapp-basic を選択した後コンテキストメニューを表示し、「Run 'Tests in 'ksbysample...' with Coverage」を選択してテストを実行しますが、まだ失敗するテストがありました。

  17. ログを見ると Caused by: org.hibernate.HibernateException: Access to DialectResolutionInfo cannot be null when 'hibernate.dialect' not set というメッセージが出力されています。いろいろ試してみた結果、テストメソッドの最後に System.setProperty("spring.profiles.active", ""); と spring.profiles.active の値をクリアしていたのが原因でした。最初に現在値を変数に保存しておき、最後に元の値に戻すようにします。

  18. src/test/java/ksbysample/webapp/basic の下の ApplicationTest.javaリンク先のその2の内容に変更します。他に気づいた点も修正します。

  19. 再度 Project View の一番上の階層の ksbysample-webapp-basic を選択した後コンテキストメニューを表示し、「Run 'Tests in 'ksbysample...' with Coverage」を選択してテストを実行します。今度は @Ignore アノテーションを付加しているテストを除き成功しました。

    f:id:ksby:20150304012909p:plain

  20. src/test/java/ksbysample/webapp/basic の下の ApplicationTest.javaリンク先のその3の内容に変更します。今回はコメントアウトではなく、テストクラスに @Ignore アノテーションを付加してテスト対象外にします。

build タスク実行、確認

  1. Gradle tasks View から build タスクを実行しましたが、BUILD FAILED が表示されました。ログを見ると、テストクラスで Caused by: java.lang.ClassNotFoundException が発生しているようです。

  2. 詳細な原因を確認するために Gradle tasks View から test タスクを実行します。実行後ログを見ると Caused by: java.lang.NoClassDefFoundError: org/springframework/security/context/DelegatingApplicationListener というメッセージが出力されていました。

  3. build.gradle に testCompile("org.springframework.security:spring-security-test:4.0.0.M2") という記述を入れているので、spring-security-test のバージョンが古いことが原因と思われます。最新バージョンを確認してみると、4.0.0.RC2 でした ( https://bintray.com/bintray/jcenter/org.springframework.security%3Aspring-security-test/view )。

  4. build.gradle をリンク先のその3の内容に変更します。

  5. 画面右側の Tool Window から「Gradle」をクリックして Gradle tasks View を表示します。View 表示後、View の左上にある「Refresh Gradle projects」アイコンをクリックして、変更した build.gradle の内容を反映します。

  6. 再度 Gradle tasks View から build タスクを実行すると、今度は BUILD SUCCESSFUL が表示されました。

  7. Project View の一番上の階層の ksbysample-webapp-basic を選択した後コンテキストメニューを表示し、「Run 'Tests in 'ksbysample...' with Coverage」を選択してテストを実行し、こちらもテストが成功することを確認しておきます。

GitHub へ commit&push

  1. commit、GitHub へ Push します。

ソースコード

build.gradle

■その1

dependencies {
    compile("org.springframework.boot:spring-boot-starter-web:1.2.2.RELEASE")
    compile("org.springframework.boot:spring-boot-starter-thymeleaf")
    compile("org.springframework.boot:spring-boot-starter-data-jpa")
    compile("org.springframework.boot:spring-boot-starter-security")
    compile("mysql:mysql-connector-java:5.1.34")
    compile("org.mybatis:mybatis:3.2.8")
    compile("org.mybatis:mybatis-spring:1.2.2")
    compile("org.bgee.log4jdbc-log4j2:log4jdbc-log4j2-jdbc4.1:1.16")
    compile("org.codehaus.janino:janino:2.7.5")
    compile("org.apache.commons:commons-lang3:3.3.2")
    compile("org.projectlombok:lombok:1.14.8")
    testCompile("org.springframework.boot:spring-boot-starter-test")
    testCompile("org.springframework.security:spring-security-test:4.0.0.M2")
    testCompile("org.dbunit:dbunit:2.5.0")
}
  • dependencies 内の org.springframework.boot:spring-boot-starter-web に指定するバージョン番号を 1.2.1.RELEASE1.2.2.RELEASE へ変更します。

■その2

dependencies {
    def springBootVersion = '1.2.2.RELEASE'

    compile("org.springframework.boot:spring-boot-starter-web:${springBootVersion}")
    compile("org.springframework.boot:spring-boot-starter-thymeleaf:${springBootVersion}")
    compile("org.springframework.boot:spring-boot-starter-data-jpa:${springBootVersion}")
    compile("org.springframework.boot:spring-boot-starter-security:${springBootVersion}")
    compile("mysql:mysql-connector-java:5.1.34")
    compile("org.mybatis:mybatis:3.2.8")
    compile("org.mybatis:mybatis-spring:1.2.2")
    compile("org.bgee.log4jdbc-log4j2:log4jdbc-log4j2-jdbc4.1:1.16")
    compile("org.codehaus.janino:janino:2.7.5")
    compile("org.apache.commons:commons-lang3:3.3.2")
    compile("org.projectlombok:lombok:1.14.8")
    testCompile("org.springframework.boot:spring-boot-starter-test:${springBootVersion}")
    testCompile("org.springframework.security:spring-security-test:4.0.0.M2")
    testCompile("org.dbunit:dbunit:2.5.0")
}
  • def springBootVersion = '1.2.2.RELEASE' を追加します。
  • spring-boot-starter-... で定義しているものは、末尾に :${springBootVersion} を追加してバージョンを明示するようにします。

■その3

dependencies {
    def springBootVersion = '1.2.2.RELEASE'

    compile("org.springframework.boot:spring-boot-starter-web:${springBootVersion}")
    compile("org.springframework.boot:spring-boot-starter-thymeleaf:${springBootVersion}")
    compile("org.springframework.boot:spring-boot-starter-data-jpa:${springBootVersion}")
    compile("org.springframework.boot:spring-boot-starter-security:${springBootVersion}")
    compile("mysql:mysql-connector-java:5.1.34")
    compile("org.mybatis:mybatis:3.2.8")
    compile("org.mybatis:mybatis-spring:1.2.2")
    compile("org.bgee.log4jdbc-log4j2:log4jdbc-log4j2-jdbc4.1:1.16")
    compile("org.codehaus.janino:janino:2.7.5")
    compile("org.apache.commons:commons-lang3:3.3.2")
    compile("org.projectlombok:lombok:1.14.8")
    testCompile("org.springframework.boot:spring-boot-starter-test:${springBootVersion}")
    testCompile("org.springframework.security:spring-security-test:4.0.0.RC2")
    testCompile("org.dbunit:dbunit:2.5.0")
}
  • testCompile("org.springframework.security:spring-security-test:4.0.0.M2")testCompile("org.springframework.security:spring-security-test:4.0.0.RC2") に変更します。

ApplicationTest.java

■その1

package ksbysample.webapp.basic;

import org.junit.Rule;
import org.junit.Test;
import org.springframework.boot.test.OutputCapture;

import static org.hamcrest.CoreMatchers.is;
import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertThat;
import static org.junit.Assert.assertTrue;

public class ApplicationTest {

    @Rule
    public OutputCapture outputCapture = new OutputCapture();

    @Test
    public void testApplicationProductMode() {
        System.setProperty("spring.profiles.active", "product");
        Application.main(new String[]{"--server.port=8081"});
        String output = this.outputCapture.toString();
        assertTrue(output, output.contains("Started Application"));
        System.setProperty("spring.profiles.active", "");
    }

    @Test
    public void testApplicationDevelopMode() {
        System.setProperty("spring.profiles.active", "develop");
        Application.main(new String[]{"--server.port=8082"});
        String output = this.outputCapture.toString();
        assertTrue(output, output.contains("Started Application"));
        System.setProperty("spring.profiles.active", "");
    }

    @Test(expected = Exception.class)
    public void testApplicationEmptyMode() {
        Application.main(new String[]{"--server.port=8083"});
        String output = this.outputCapture.toString();
        assertFalse(output, output.contains("Started Application"));
        assertTrue(output, output.contains("JVMの起動時引数 -Dspring.profiles.active で develop か product を指定して下さい"));
    }

}
  • testApplicationProductMode, testApplicationDevelopMode メソッドに付加するアノテーション@Test(expected = Exception.class)@Test に変更します。

■その2

package ksbysample.webapp.basic;

import org.junit.Ignore;
import org.junit.Rule;
import org.junit.Test;
import org.springframework.boot.test.OutputCapture;

import static org.hamcrest.CoreMatchers.is;
import static org.junit.Assert.assertThat;

public class ApplicationTest {

    @Rule
    public OutputCapture outputCapture = new OutputCapture();

    @Test
    public void testApplicationProductMode() {
        String SPRING_PROFILES_ACTIVE = System.getProperty("spring.profiles.active");
        System.setProperty("spring.profiles.active", "product");
        Application.main(new String[]{"--server.port=8081"});
        String output = this.outputCapture.toString();
        assertThat(output.contains("Started Application"), is(true));
        System.setProperty("spring.profiles.active", SPRING_PROFILES_ACTIVE);
    }

    @Test
    public void testApplicationDevelopMode() {
        String SPRING_PROFILES_ACTIVE = System.getProperty("spring.profiles.active");
        System.setProperty("spring.profiles.active", "develop");
        Application.main(new String[]{"--server.port=8082"});
        String output = this.outputCapture.toString();
        assertThat(output.contains("Started Application"), is(true));
        System.setProperty("spring.profiles.active", SPRING_PROFILES_ACTIVE);
    }

    @Test
    public void testApplicationEmptyMode() {
        String SPRING_PROFILES_ACTIVE = System.getProperty("spring.profiles.active");
        System.setProperty("spring.profiles.active", "");
        try {
            Application.main(new String[]{"--server.port=8083"});
        }
        catch (Exception e) {
            e.printStackTrace();
        }
        String output = this.outputCapture.toString();
        assertThat(output.contains("Started Application"), is(false));
        assertThat(output.contains("JVMの起動時引数 -Dspring.profiles.active で develop か product を指定して下さい"), is(true));
        System.setProperty("spring.profiles.active", SPRING_PROFILES_ACTIVE);
    }

}
  • 全てのメソッドで、処理の最初に String SPRING_PROFILES_ACTIVE = System.getProperty("spring.profiles.active"); を、処理の最後に System.setProperty("spring.profiles.active", SPRING_PROFILES_ACTIVE); を追加し、元の値に戻すようにします。
  • assertTrue, assertFalse でチェックしていた部分を assertThat に変更します。
  • testApplicationEmptyMode メソッドに付加するアノテーション@Test(expected = Exception.class)@Test に変更し、メソッド内で Application.main(new String[]{"--server.port=8083"}); を try ... catch で囲み、Exception 発生時に spring.profiles.active の値を元に戻さずにテストメソッドが終了しないようにします。

■その3

@Ignore
public class ApplicationTest {
  • ApplicationTest クラス名の上に @Ignore アノテーションを追加します。これでこのクラス全体がテスト対象外になります。

履歴

2015/03/04
初版発行。