お題
前々回、GAE用のJava8アプリをMaven自動生成して、前回は主な修正対象のファイルについて説明。
今回から、とりあえずDatastoreへのアクセスロジック入れていこうかと思ったけど、その前にテストコードについても触れておこう。
一応、テストファーストで進めていく予定なので。
GAE試行Index
- GAE/Java8試行(その0:「App Engineについて」)
- GAE/Java8試行(その1:「Java8でWebアプリ作ってデプロイ」)
- GAE/Java8試行(その2:「Javaアプリ解説」)
開発環境
# 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
解説(というか自分自身のおさらい)
自動生成されたテストコードは「HelloAppEngineTest.java」
$ tree ※必要な部分だけ抜粋
.
└── sky0621
├── pom.xml
└── src
└── test
└── java
└── com
└── example
└── sky0621
└── HelloAppEngineTest.java
以降、説明に必要な部分を抜粋しつつまとめる。
■JUnit4とmochito
@RunWith(JUnit4.class)
public class HelloAppEngineTest {
〜省略〜
@Mock private HttpServletRequest mockRequest;
@Mock private HttpServletResponse mockResponse;
〜省略〜
App Engine用のMavenプロジェクトを自動生成すると、「pom.xml」で以下のVersionで定義される。
<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」が出てるけど、ここでは大人しく、指定されたバージョンを使う。
Mockitoは、Javaのテストコードでモックを使うときの定番。
モック化したいオブジェクトの頭に「@Mock
」アノテーションを付けるだけなので簡単。
■テストケース実行前処理
@Before
public void setUp() throws Exception {
〜省略〜
}
「@Before
」付加により、各テストケース実行前に行うべき処理を定義できる。
when(mockRequest.getRequestURI()).thenReturn(FAKE_URL);
when(mockResponse.getWriter()).thenReturn(new PrintWriter(responseWriter));
先ほど見た「@Mock
」アノテーションを付けたオブジェクトは、こうすることで挙動を操作できる。
(説明なくても、なんとなく意味はわかると思う。)
■LocalServiceTestHelper
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といったサービスにアクセス(=通信を伴う)する際に接続先をスタブにしてくれる(=通信レス)。
こうすることで、通信先がある環境かどうかに依存しないテストコードが書ける。
(テストコードは、どこでもなんどやっても同じ結果になる冪等性が大事)
■テストケース実行後処理
@After public void tearDown() {
helper.tearDown();
}
「@After
」付加により、各テストケース実行後に行うべき処理を定義できる。
ここでは、セットアップしたLocalServiceTestHelper
の後始末を行う。
■テストケース
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アクセスコードを書こう。。。