0
1

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

More than 5 years have passed since last update.

GAE/Java8試行(その4:「Datastoreへのアクセスロジック」)

Last updated at Posted at 2018-10-22

お題

前回、GAE用のJava8アプリをMaven自動生成した際にできるテストコードについて触れた。
今回は、テストファーストを意識して、Datastoreアクセスロジックの追加を試みる。

GAE試行Index

開発環境

# 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

参考

実践

■設計

これから実装する機能の仕様を決める。
テーマは、GCPのチュートリアルでもよくある「書籍リストの管理」システムとする。
今回Datastoreへのアクセスロジックを試す機能としては、
「書籍名」をPOSTしたらDatastoreに登録されるものとする。

■テストコード

仕様に合わせて、下記のようなテストコードを書いてみる。
モックリクエストを操作し、「書籍名」がリクエストパラメータから取得できるようにする。
そして、 doPost(~~) 実行で上記の「書籍名」が book カインドに登録されていることを検証する。

  @Test
  public void 書籍名をPOSTするとDatastoreに登録される() throws EntityNotFoundException, ServletException, IOException {
    /*
     * SetUp
     */
    Map<String, String[]> parameterMap = new HashMap<>();
    parameterMap.put("bookName", new String[]{"マイクロサービスアーキテクチャ"});
    when(mockRequest.getParameterMap()).thenReturn(parameterMap);

    /*
     * Execute
     */
    servletUnderTest.doPost(mockRequest, mockResponse);

    /*
     * Assert
     */
    DatastoreService ds = DatastoreServiceFactory.getDatastoreService();
    Entity e = ds.get(KeyFactory.createKey("book", 1));
    String microService = (String)e.getProperty("bookName");
    assertThat(microService).isEqualTo("マイクロサービスアーキテクチャ");
  }

ちなみに、テストファーストなので doPost(~~) のない状態では上記テストコードはコンパイルエラー。
その後、 doPost(~~) の定義だけ書いて、テストコードを実行すると、下記のようになる。
リクエストパラメータとして渡した「書籍名」をDatastoreに登録するロジックは未実装なので、当然の結果。
テストファーストでは、まず、仕様を決めて、それを確認するテストコードを書く。
(その時点ではコンパイルも通らないが、そこから始めることが重要)
最初にテストに失敗させる。
失敗することがわかっているのに、あえて失敗させる。ロジック実装前なので、テスト実行結果がNGとなる。
これを最初に確認することで、ロジック実装後にテスト実行結果がOKとなることに意味が出る。
(最初に失敗することを確認しないと、正しい検証コードが書けていなくて、そもそも最初からテスト実行結果がOKだったかもしれず、正しく実装できていることをテストできていないテストコードになる可能性があるため)


com.google.appengine.api.datastore.EntityNotFoundException: No entity was found matching the key: book("micro")

	at com.google.appengine.api.datastore.BaseAsyncDatastoreServiceImpl$1.wrap(BaseAsyncDatastoreServiceImpl.java:174)
	at com.google.appengine.api.datastore.BaseAsyncDatastoreServiceImpl$1.wrap(BaseAsyncDatastoreServiceImpl.java:169)
	at com.google.appengine.api.utils.FutureWrapper.wrapAndCache(FutureWrapper.java:56)
	at com.google.appengine.api.utils.FutureWrapper.get(FutureWrapper.java:93)
	at com.google.appengine.api.datastore.FutureHelper.getInternal(FutureHelper.java:76)
	at com.google.appengine.api.datastore.FutureHelper.quietGet(FutureHelper.java:63)
	at com.google.appengine.api.datastore.DatastoreServiceImpl.get(DatastoreServiceImpl.java:41)
	at com.example.sky0621.HelloAppEngineTest.書籍名をPOSTするとDatastoreに登録される(HelloAppEngineTest.java:100)
	at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
	at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62)
	at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
	at java.lang.reflect.Method.invoke(Method.java:498)
	at org.junit.runners.model.FrameworkMethod$1.runReflectiveCall(FrameworkMethod.java:50)
	at org.junit.internal.runners.model.ReflectiveCallable.run(ReflectiveCallable.java:12)
	at org.junit.runners.model.FrameworkMethod.invokeExplosively(FrameworkMethod.java:47)
	at org.junit.internal.runners.statements.InvokeMethod.evaluate(InvokeMethod.java:17)
	at org.junit.internal.runners.statements.RunBefores.evaluate(RunBefores.java:26)
	at org.junit.internal.runners.statements.RunAfters.evaluate(RunAfters.java:27)
	at org.junit.runners.ParentRunner.runLeaf(ParentRunner.java:325)
	at org.junit.runners.BlockJUnit4ClassRunner.runChild(BlockJUnit4ClassRunner.java:78)
	at org.junit.runners.BlockJUnit4ClassRunner.runChild(BlockJUnit4ClassRunner.java:57)
	at org.junit.runners.ParentRunner$3.run(ParentRunner.java:290)
	at org.junit.runners.ParentRunner$1.schedule(ParentRunner.java:71)
	at org.junit.runners.ParentRunner.runChildren(ParentRunner.java:288)
	at org.junit.runners.ParentRunner.access$000(ParentRunner.java:58)
	at org.junit.runners.ParentRunner$2.evaluate(ParentRunner.java:268)
	at org.junit.runners.ParentRunner.run(ParentRunner.java:363)
	at org.junit.runner.JUnitCore.run(JUnitCore.java:137)
	at com.intellij.junit4.JUnit4IdeaTestRunner.startRunnerWithArgs(JUnit4IdeaTestRunner.java:68)
	at com.intellij.rt.execution.junit.IdeaTestRunner$Repeater.startRunnerWithArgs(IdeaTestRunner.java:47)
	at com.intellij.rt.execution.junit.JUnitStarter.prepareStreamsAndStart(JUnitStarter.java:242)
	at com.intellij.rt.execution.junit.JUnitStarter.main(JUnitStarter.java:70)

10 23, 2018 12:21:13 午前 com.google.appengine.api.datastore.dev.LocalDatastoreService cleanupActiveServices
情報: scheduler shutting down.
Disconnected from the target VM, address: '127.0.0.1:37713', transport: 'socket'

Process finished with exit code 255

さて、想定通りのテスト失敗となったので、いよいよDatastore登録ロジックを実装する。

■実装

こんな感じ。
プロダクトコードで使えるレベルではないけど、とりあえずこれだけでリクエストパラメータから「書籍名」を捕まえてDatastoreni登録するコードになる。


  @Override
  protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
    DatastoreService ds = DatastoreServiceFactory.getDatastoreService();

    req.getParameterMap().forEach((k, v) -> {
      Key key = KeyFactory.createKey("book", 1);
      Entity e = new Entity(key);
      e.setProperty("bookName", Arrays.stream(v).collect(Collectors.joining()));
      ds.put(e);
    });
  }

この実装後に先ほどのテストコードを流すと、今度はテスト結果OKとなる。

まとめ

テストファーストでやると、ちょっとした機能の実装だけでも、それなりに時間がかかるようになる。
ただ、”それなりの時間”には、仕様の検討、実装、単体テスト、リファクタ、手戻り削減コストなどなどが含まれているので、本当のところはコスパ的には良い。

0
1
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
0
1

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?