GAE/Java8試行（その3：「Javaアプリテストコード解説」）

# お題
[前々回](https://qiita.com/sky0621/items/8437da4e96bffef69250)、GAE用のJava8アプリをMaven自動生成して、[前回](https://qiita.com/sky0621/items/65ce0406fb895f547ae5)は主な修正対象のファイルについて説明。
今回から、とりあえずDatastoreへのアクセスロジック入れていこうかと思ったけど、その前にテストコードについても触れておこう。
一応、テストファーストで進めていく予定なので。

# GAE試行Index
- [GAE/Java8試行（その0：「App Engineについて」）](https://qiita.com/sky0621/items/6175d578d0521858a944)
- [GAE/Java8試行（その1：「Java8でWebアプリ作ってデプロイ」）](https://qiita.com/sky0621/items/8437da4e96bffef69250)
- [GAE/Java8試行（その2：「Javaアプリ解説」）](https://qiita.com/sky0621/items/65ce0406fb895f547ae5)

# 開発環境
### # OS
```
$ cat /etc/os-release 
NAME="Ubuntu"
VERSION="17.10 (Artful Aardvark)"
```

### # Java
```
$ java -version
java version "1.8.0_181"
Java(TM) SE Runtime Environment (build 1.8.0_181-b13)
Java HotSpot(TM) 64-Bit Server VM (build 25.181-b13, mixed mode)
```
### # IDE
みんな大好き[IntelliJ IDEA](https://www.jetbrains.com/idea/)

# 解説（というか自分自身のおさらい）
自動生成されたテストコードは「HelloAppEngineTest.java」

```
$ tree ※必要な部分だけ抜粋
.
└── sky0621
    ├── pom.xml
    └── src
         └── test
             └── java
                 └── com
                     └── example
                         └── sky0621
                             └── HelloAppEngineTest.java
```

ソースコードの全量は↓に。
https://github.com/sky0621/java-webapi-for-gae-study/blob/1a9d408da2a670ac2d61416d6ec964ae4306c8b8/sky0621/src/test/java/com/example/sky0621/HelloAppEngineTest.java

以降、説明に必要な部分を抜粋しつつまとめる。

## ■JUnit4とmochito

```java
@RunWith(JUnit4.class)
public class HelloAppEngineTest {
　　〜省略〜
  @Mock private HttpServletRequest mockRequest;
  @Mock private HttpServletResponse mockResponse;
　　〜省略〜
```

App Engine用のMavenプロジェクトを自動生成すると、「pom.xml」で以下のVersionで定義される。

```xml
    <dependency>
      <groupId>junit</groupId>
      <artifactId>junit</artifactId>
      <version>4.12</version>
      <scope>test</scope>
    </dependency>
    <dependency>
      <groupId>org.mockito</groupId>
      <artifactId>mockito-all</artifactId>
      <version>1.10.19</version>
      <scope>test</scope>
    </dependency>
```

JUnitは、最新という意味では「[JUnit5](https://junit.org/junit5/)」が出てるけど、ここでは大人しく、指定されたバージョンを使う。
[Mockito](https://site.mockito.org)は、Javaのテストコードでモックを使うときの定番。
モック化したいオブジェクトの頭に「`@Mock`」アノテーションを付けるだけなので簡単。

## ■テストケース実行前処理

```java
  @Before
  public void setUp() throws Exception {
　　〜省略〜
  }
```

「`@Before`」付加により、各テストケース実行前に行うべき処理を定義できる。

```java
    when(mockRequest.getRequestURI()).thenReturn(FAKE_URL);
    when(mockResponse.getWriter()).thenReturn(new PrintWriter(responseWriter));
```

先ほど見た「`@Mock`」アノテーションを付けたオブジェクトは、こうすることで挙動を操作できる。
（説明なくても、なんとなく意味はわかると思う。）

## ■LocalServiceTestHelper

```java
import com.google.appengine.tools.development.testing.LocalServiceTestHelper;
　　〜省略〜
  private final LocalServiceTestHelper helper = new LocalServiceTestHelper();
　　〜省略〜
  @Before
  public void setUp() throws Exception {
    helper.setUp();
　　　〜省略〜
  }
```
`LocalServiceTestHelper`を各テストケース実行前にセットアップしておくことで、`appengine`のライブラリを使ってGAEからDatastoreないしMemcacheといったサービスにアクセス（＝通信を伴う）する際に接続先をスタブにしてくれる（＝通信レス）。
こうすることで、通信先がある環境かどうかに依存しないテストコードが書ける。
（テストコードは、どこでもなんどやっても同じ結果になる冪等性が大事）

## ■テストケース実行後処理

```java
  @After public void tearDown() {
    helper.tearDown();
  }
```

「`@After`」付加により、各テストケース実行後に行うべき処理を定義できる。
ここでは、セットアップした`LocalServiceTestHelper`の後始末を行う。

## ■テストケース

```java
import static com.google.common.truth.Truth.assertThat;
　　〜省略〜
  @Mock private HttpServletRequest mockRequest;
  @Mock private HttpServletResponse mockResponse;
  private StringWriter responseWriter;
  private HelloAppEngine servletUnderTest;
　　〜省略〜
  @Test
  public void doGet_writesResponse() throws Exception {
    servletUnderTest.doGet(mockRequest, mockResponse);

    // We expect our hello world response.
    assertThat(responseWriter.toString())
        .named("HelloAppEngine response")
        .contains("Hello App Engine - Standard ");
  }
```

モックを渡して、テスト対象である「`HelloAppEngine#doGet`」のテストを行う。
結果の検証には「`assertThat`」を使う。
↑はJUnitのものではなくGoogleが提供しているパッケージを使う。

# まとめ
ひとまず（当然のことながら）自動生成されたテストコードの時点では冪等性のあるものになっている。
ここを崩さないようにしつつ、テスタブルなプロダクトコードを書いていく。
次こそは、Datastoreアクセスコードを書こう。。。