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

## はじめに

こんにちは！[macでSTSの環境構築を行った](https://qiita.com/akane_kato/items/fff46dfa34f97f95dc88)者です。
今回は「HelloWorldファイルとHelloWorldのテストコードファイルを作成し、jarに保存してmacで実行してみる」ところまで実施しました。
前回同様、[Spring徹底入門](https://www.shoeisha.co.jp/book/detail/9784798142470)を参考に進めました。簡単な手順ですが、バージョンのせいか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](https://start.spring.io/)でプロジェクトの雛形を作成します。
Spring InitializrはSpring Bootが提供しているWebサービスです。必要事項をチェックするだけで、Spring Bootプロジェクト作成の基礎となるディレクトリがzip形式でダウンロードできます。また、メインメソッドの雛形があるDemoApplication.javaと、テストコードの雛形があるDemoApplicationTests.javaが、あらかじめ適切なディレクトリ配下に配置されています。

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

<img width="1362" alt="スクリーンショット 2020-05-20 22.49.42.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/469936/1343e2bd-0c63-8290-afe9-072d91acf1e3.png">

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

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

<img width="317" alt="スクリーンショット 2020-05-26 0.47.04.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/469936/81ec9dc1-a4c2-62f4-692c-84e7d8fa193f.png">

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

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

<img width="321" alt="スクリーンショット 2020-05-26 1.14.17.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/469936/f1c2d651-3622-b05e-9416-f2e37155e8cc.png">

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

```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部分のみを実装していきたいと思います。

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

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

### ■`@RequestMapping`、`@ResponseBody`を付与したメソッドhelloWorld()を作成する
`@RequestMapping`と`@ResponseBody`、二つのアノテーションを付与することで、戻り値をレスポンスのコンテンツとすることができます。
参考：[Spring MVCのコントローラでの戻り値いろいろ](https://qiita.com/tag1216/items/3680b92cf96eb5a170f0)

```java

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 でハロワできるはずです！やったね！

<img width="280" alt="スクリーンショット 2020-05-26 2.12.34.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/469936/84504d95-6532-3d79-23c7-2dc8e8db860c.png">


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

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

<img width="725" alt="スクリーンショット 2020-05-26 1.38.40.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/469936/2ba35ef0-59a8-d198-0de9-012d198376c8.png">

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

```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`がついているクラスを[テスト用のコンフィグレーションクラスとして認識します。](https://qiita.com/BooookStore/items/14c7bd559878991cf112)

### ■`@RunWith(SpringRunner.class)`の代わりに`@ExtendWith(SpringExtension.class)`を使用する
SpringRunnerはJUnit4のものなので、JUnit5で使用するとその後のjar作成時にビルドがうまく通らなくなりました…
JUnit5では`@RunWith`アノテーション自体が`@ExtendWith`に置き換えられていたので、そちらに変更しました。[JUnit5 @RunWith](https://www.codeflow.site/ja/article/junit-5-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は[非推奨](https://junit.org/junit4/javadoc/latest/deprecated-list.html)と出て、deprecatedを許容するアノテーション`@SuppressWarnings(“deprecated”)`を付与してもうまくjarのビルドができませんでした…（上記以外が原因かもです）
代わりに、hamcrestのassertThatを使用しました。

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


```java

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パネルに以下のように表示されれば、テスト成功です！

<img width="457" alt="スクリーンショット 2020-05-26 3.26.19.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/469936/89626f94-5337-7fcc-088b-2d411bb8afdd.png">

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

<img width="463" alt="スクリーンショット 2020-05-26 3.28.54.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/469936/cee511d5-94c6-d1c3-b489-4923a851b737.png">

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

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

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

```
cd ~/Users/xxx/workspace/demo
```

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

```
./mvnw clean package
```

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

<img width="551" alt="スクリーンショット 2020-05-26 3.40.11.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/469936/e813f6fc-e450-2548-c3af-9ab46b09ddd9.png">

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

<img width="332" alt="スクリーンショット 2020-05-26 3.46.31.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/469936/ea1c3a96-d845-268c-6679-812c37f25db5.png">

### ③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!が表示できます。

<img width="847" alt="スクリーンショット 2020-05-26 3.52.32.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/469936/c86cfa4d-6ef5-e5b0-65bf-94367aa8ddf6.png">

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

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