【Java】Java14×Spring Boot2.3×JUnit5でHelloWorldしたよ〜


はじめに

こんにちは!macでSTSの環境構築を行った者です。
今回は「HelloWorldファイルとHelloWorldのテストコードファイルを作成し、jarに保存してmacで実行してみる」ところまで実施しました。
前回同様、Spring徹底入門を参考に進めました。簡単な手順ですが、バージョンのせいかJUnitで実行する辺りやjarにする辺りで思いの外詰まったので記事にしました。
ぜひ俺の屍を越えていってください。

使用環境とバージョン

  • macOS Catalina
  • jdk14.0.1
  • JUnit5
  • Maven 3.6.3_1
  • STS 4.6.1
  • Spring Boot 2.3

Spring Initializrでプロジェクトの雛形を作る

まずSpring Initializrでプロジェクトの雛形を作成します。
Spring InitializrはSpring Bootが提供しているWebサービスです。必要事項をチェックするだけで、Spring Bootプロジェクト作成の基礎となるディレクトリがzip形式でダウンロードできます。また、メインメソッドの雛形があるDemoApplication.javaと、テストコードの雛形があるDemoApplicationTests.javaが、あらかじめ適切なディレクトリ配下に配置されています。

つまり、ダウンロードするだけで今回の作成物の8割は完成します笑

前回Mavenで環境構築を行ったので、ProjectはMavenを選びます。
その他、言語やSpringBootのバージョン、Javaのバージョンなど、自分に合ったものを選択し、GENERATEボタンをクリックすると、demo.zipのダウンロードが始まります。

demo.zipを解凍すると、以下のようなMavenプロジェクトが既に作成されています。すごい!めちゃめちゃ簡単じゃないですか!!

解凍したdemoファイルは、環境構築した際に作成したworkspaceディレクトリ配下に移動させておくと進めやすいです。
STSを開き、File > Import > Existing Maven Projectsで、先ほど解凍したdemoフォルダを選択すれば、インポートを行えます。

自動的にビルドが走るので、Package Explorerが以下画像のようにパッケージ表示になるまでしばし待ちます。自動で以下のようにならなかったら、メニューバーからProject > Cleanを押下してください。

DemoApplication.javaの編集

ダウンロードしたばかりのDemoApplication.javaの中身は以下のようになっているはずです。


package com.example.demo;

import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;

@SpringBootApplication
public class DemoApplication {

    public static void main(String[] args) {
        SpringApplication.run(DemoApplication.class, args);
    }

}

SpringはMVCモデルのフレームワークです。HelloWorldプロジェクトは最低限の実装で良いので、クライアントからのリクエストを受け取ってレスポンスを返す1、Controller部分のみを実装していきたいと思います。

■DemoApplicationクラスに@Controllerをつける

言わずもがなですが、@Controllerアノテーションをつければ、そのクラスをコントローラーとすることができます。

@RequestMapping@ResponseBodyを付与したメソッドhelloWorld()を作成する

@RequestMapping@ResponseBody、二つのアノテーションを付与することで、戻り値をレスポンスのコンテンツとすることができます。
参考:Spring MVCのコントローラでの戻り値いろいろ


package com.example.demo;

import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.ResponseBody;

@SpringBootApplication
@Controller
public class DemoApplication {

    @RequestMapping("/")
    @ResponseBody
    String helloWorld() {
        return "Hello World!";
    }

    public static void main(String[] args) {
        SpringApplication.run(DemoApplication.class, args);
    }

}

保存して、DemoApplicationを右クリック > Run As > Java Applicationと選択してメインメソッドを実行すると、http://localhost:8080 でハロワできるはずです!やったね!

上記以外の書き方でも、コントローラーのアノテーションを@Controllerから@RestControllerに変更すれば、helloWorld()のアノテーションは@RequestMappingのみで動きます。

ただし、アノテーションの組み合わせを間違えるとエラーが発生します(やらかしました)。どうやらHTTPリクエストがアプリケーション側で受け取れていないとこの画面になるようで、DemoApplication.javaをダウンロードしたまんまで実行しても同じ画面になるそうです。

DemoApplicationTests.javaの編集

DemoApplication.javaの実装に合わせて、テストコードも編集していきます。以下がダウンロードしたまんまのテストコードです。これに手を加えていきます。


package com.example.demo;

import org.junit.jupiter.api.Test;
import org.springframework.boot.test.context.SpringBootTest;

@SpringBootTest
class DemoApplicationTests {

    @Test
    void contextLoads() {
    }

}

@SpringApplicationConfigration@WebIntegrationTestの代わりに@SpringBootTestを付与する

前者2つのアノテーションはSpring Boot 1.4から非推奨になったため、@SpringBootTestに置き換えます。
@SpringBootTest@SpringBootApplicationがついているクラスをテスト用のコンフィグレーションクラスとして認識します。

@RunWith(SpringRunner.class)の代わりに@ExtendWith(SpringExtension.class)を使用する

SpringRunnerはJUnit4のものなので、JUnit5で使用するとその後のjar作成時にビルドがうまく通らなくなりました…
JUnit5では@RunWithアノテーション自体が@ExtendWithに置き換えられていたので、そちらに変更しました。JUnit5 @RunWith

@LocalServerPortでポート番号を取得する

Spring Boot 1.3以前では@Value(“${local.server.port}”)で取得していたポート番号ですが、1.4以降@LocalServerPortがショートカットとして追加されています。2.3だと@Valueでポート番号を取得しようとすると以下のエラーが発生して、テスト実行ができませんでした。。

org.springframework.beans.factory.UnsatisfiedDependencyException: Error creating bean with name 'com.example.demo.DemoApplicationTests': Unsatisfied dependency expressed through field 'port';
nested exception is org.springframework.beans.TypeMismatchException: Failed to convert value of type 'java.lang.String' to required type 'int'; 
nested exception is java.lang.NumberFormatException: For input string: "${local.server.port"

■assertThatメソッドはorg.junit.Assert.assertThatではなくorg.hamcrest.MatcherAssert.assertThatを使用する

JUnitのassertThatは非推奨と出て、deprecatedを許容するアノテーション@SuppressWarnings(“deprecated”)を付与してもうまくjarのビルドができませんでした…(上記以外が原因かもです)
代わりに、hamcrestのassertThatを使用しました。

最終的なテストコードは以下のようになりました。


package com.example.demo;

import static org.hamcrest.CoreMatchers.is;

import static org.hamcrest.MatcherAssert.assertThat;

import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.extension.ExtendWith;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.boot.test.web.client.TestRestTemplate;
import org.springframework.boot.web.server.LocalServerPort;
import org.springframework.test.context.junit.jupiter.SpringExtension;

@ExtendWith(SpringExtension.class)
@SpringBootTest(webEnvironment=SpringBootTest.WebEnvironment.RANDOM_PORT)
public class DemoApplicationTests {

    TestRestTemplate restTemplate = new TestRestTemplate();

    @LocalServerPort
    int port;

    @Test
    public void testHello() {
        assertThat(restTemplate.getForObject("http://localhost:" + port, String.class), is("Hello World!"));
    }

}

DemoApplicationTests.javaを右クリック > Run As > JUnit Testでテストコードが実行できます。JUnitパネルに以下のように表示されれば、テスト成功です!

上記テストコードかDemoApplication.javaのどちらかの"Hello World!"部分を別の言葉に変えて、ちゃんとテストが失敗するかも確認してみてください。

実行可能jarを作成する

ここまでの工程で、既にSpring プロジェクトのHelloWorldは出来上がっています。しかし、実際にjarにしてみると、コンパイルやビルドがうまくいかないことに気づけるので、やっておくと吉です!

jarを作成する手順を説明していきます。

①macのターミナルから、ワークスペース配下のdemoディレクトリまで移動する

cd ~/Users/xxx/workspace/demo

②実行可能jarを作成するMavenコマンドを実行する

./mvnw clean package

STS上ではHelloWorldもテストもうまく行っていたとしても、ここでエラーが出ることもあります(自分です)
自分はここで@RunWithが使えないことを知りました。

うまくいった場合は、下図のようにBUILD SUCCESSが表示されます。

③jarを実行する

無事にビルドが成功したら、demo/target 配下に「demo-0.0.1-SNAPSHOT.jar」ができているはずです。末尾にoriginalとついていない方を実行します。

java -jar /Users/xxx/workspace/demo/target/demo-0.0.1-SNAPSHOT.jar

以下のようにSpringが立ち上がれば成功です! http://localhost:8080 で同じようにHello World!が表示できます。

おわりに

冒頭に記した通り、Spring徹底入門を読みながら実施しましたが、ただのHelloWorldを作る際に、同じJavaやIDEでも、バージョンが違うだけでこんなに違うんだなと驚きました…バージョン大事と言われる理由が分かりました…
せっかく雛形を作成したので、次回は簡単にアプリを作成して、テストコードも書いてみたいと思います!

お読みいただきありがとうございました!
ここ違うかもよ〜、という箇所ありましたら、例の如く、そ〜っと教えてください…!


  1. Spring MVCの細かな説明は省きますが、正確にはコントローラーの前にあるフロントコントローラーというサーブレットが、リクエストを受け取ってレスポンスを返しています。ここはフレームワークに任せるため、開発者はコントローラーのみ実装します。