【入門編】Slim3で始める!GAE/JでWebアプリケーション開発 (第3回)

登録前のバリデーション、データストアへの登録など前回作成した<記事登録>と同等の処理を実装します。

  • Antタスク gen-controller-without-view の実行gen-controller-without-view に /bbs/postComment と入力して以下のクラスを生成します。
    • simplebbs.controller.bbs.PostCommentController
    • simplebbs.controller.bbs.PostCommentControllerTest
  • テストケースの作成まずアプリ要件であるコメントのバリデーションルールは以下の通りとします。
    入力フォーム 検証内容
    お名前 入力必須、50文字までであること
    コメント 入力必須であること (文字数制限なし)

    これらのバリデーション、およびコメントの登録をテストするために以下のようにPostCommentControllerTestを修正しました。

    PostCommentControllerTest.java

    package simplebbs.controller.bbs;
    
    import java.util.Date;
    import java.util.List;
    
    import org.slim3.datastore.Datastore;
    import org.slim3.tester.ControllerTestCase;
    import org.junit.Test;
    
    import com.google.appengine.api.datastore.Key;
    
    import simplebbs.model.bbs.Body;
    import simplebbs.model.bbs.Comment;
    import simplebbs.model.bbs.Head;
    import simplebbs.service.bbs.BbsService;
    import static org.junit.Assert.*;
    import static org.hamcrest.CoreMatchers.*;
    
    public class PostCommentControllerTest extends ControllerTestCase {
    
        // バリデーションOKな入力値
        private static final String USERNAME = "コメント投稿者名です";
        private static final String COMMENT = "コメント本文です。";
    
        private BbsService service = new BbsService();
        private String keyString = null;
    
        @Override
        // 各テストの前に実行される処理
        public void setUp() throws Exception {
            super.setUp();
            // 各テストの実行前に記事を1件登録しておく
            insertEntry("テスト用の記事", "テストユーザ", new Date(), "テスト用の記事の本文です。");
            // 登録した1件の記事を取得する
            Head head = service.getAll().get(0);
            // 記事のKeyを文字列に変換して保持
            keyString = Datastore.keyToString(head.getKey());
        }
    
        // 記事を新規登録する
        private void insertEntry(String subject, String username, Date postDate, String text) throws Exception {
            // 記事の作成
            Head head = new Head();
            head.setSubject(subject);
            head.setUsername(username);
            head.setPostDate(postDate);
            // 本文の作成
            Body body = new Body();
            body.setText(text);
            // データストアへ登録
            service.insert(head, body);
        }
    
        // 記事詳細ページでコメントを投稿した動作をエミュレート
        private PostCommentController postComment(String username, String comment) throws Exception {
            tester.param("key", keyString);
            tester.param("username", username);
            tester.param("comment", comment);
            tester.request.setMethod("POST");
            tester.start("/bbs/postComment");
            return tester.getController();
        }
        // バリデーションOKな入力値であること
        private void assertValidParameters() {
            // 記事を再取得
            Head storedHead = service.get(Datastore.stringToKey(keyString));
            // 記事のコメントリストを取得
            List<Comment> comments = storedHead.getCommentRef().getModelList();
            // 1件のコメントが登録されていること
            assertThat(comments.size(), is(1));
            // エラーメッセージが1つもセットされていないこと
            assertThat(tester.getErrors().isEmpty(), is(true));
            // 記事詳細ページにリダイレクトしていること
            assertThat(tester.getDestinationPath(), is("/bbs/read?key="+keyString));
            assertThat(tester.isRedirect(), is(true));
        }
        // バリデーションNGな入力値であること
        private void assertInvalidParameters() {
            // 記事を再取得
            Head storedHead = service.get(Datastore.stringToKey(keyString));
            // 記事のコメントリストを取得
            List<Comment> comments = storedHead.getCommentRef().getModelList();
            // 記事が登録されていないこと(0件であること)
            assertThat(comments.size(), is(0));
            // エラーメッセージが何かしらセットされていること
            assertThat(tester.getErrors().isEmpty(), is(false));
            // リクエストスコープに記事の主キーが指定されていること
            assertThat(tester.asString("key"), is(keyString));
            // 記事詳細ページにフォワードしていること
            assertThat(tester.getDestinationPath(), is("/bbs/read"));
            assertThat(tester.isRedirect(), is(false));
        }
    
        @Test
        public void testValidParameters() throws Exception {
            // -----<< すべての入力値OKのテスト >>----- //
            PostCommentController controller =
                postComment(USERNAME, COMMENT);
            // ========== assertion start ========== //
            assertThat(controller, is(notNullValue()));
            assertValidParameters(); // 入力値OK
            // ========== assertion end ========== //
        }
    
        @Test
        public void testUsernameEmpty() throws Exception {
            // -----<< 投稿者名未入力で入力値NGのテスト >>----- //
            postComment("", COMMENT);
            // ========== assertion start ========== //
            assertInvalidParameters(); // 入力値NG
            // username のエラーメッセージがセットされていること
            assertThat(tester.getErrors().get("username"), is(notNullValue()));
            // ========== assertion end ========== //
        }
    
        ~ 省略 ~
    
        @Test
        public void testAfterDeleted() throws Exception {
            // -----<< コメント時に記事が存在しない場合のテスト >>----- //
            // 主キーの文字列をKeyオブジェクトに変換
            Key key = Datastore.stringToKey(keyString);
            // 該当の記事を削除しておく
            service.delete(key);
            // -----<< 有効な入力値でコメントを投稿 >>----- //
            postComment(USERNAME, COMMENT);
            // ========== assertion start ========== //
            // 記事が削除されてるので取得できないこと
            assertThat(service.get(key), is(nullValue()));
            // Errorsのキー"message"にエラーメッセージがセットされていること
            assertThat(tester.getErrors().get("message"), is(notNullValue()));
            // トップページにforwardしていること
            assertThat(tester.getDestinationPath(), is("/bbs/"));
            assertThat(tester.isRedirect(), is(false));
            // ========== assertion end ========== //
        }
    }
    

    テスト数が多いため途中で省略しています。

    記事に対するコメント登録のため、やはり今回も setUp() メソッドにて予め記事を1件登録し、それ以降の検証は記事登録や記事更新のバリデーションのテストと同様に行います。

    全テストケースを書き終わったら実行して全て失敗することを確認しておきます。


    PostCommentControllerTest失敗イメージ

  • 必要な処理の実装PostCommentControllerを以下のように修正しました。

    PostCommentController.java

    package simplebbs.controller.bbs;
    
    import java.util.Date;
    
    import org.slim3.controller.Controller;
    import org.slim3.controller.Navigation;
    import org.slim3.controller.validator.Validators;
    import org.slim3.util.ApplicationMessage;
    import org.slim3.util.BeanUtil;
    
    import simplebbs.model.bbs.Comment;
    import simplebbs.model.bbs.Head;
    import simplebbs.service.bbs.BbsService;
    
    public class PostCommentController extends Controller {
    
        @Override
        public Navigation run() throws Exception {
            if (!isPost()) {
                // POSTではないリクエストはトップページへリダイレクト
                return redirect(basePath);
            }
            // 入力値のバリデーション
            if (!validate()) {
                // バリデーションエラーの場合は記事詳細ページを表示
                return forward("read");
            }
            BbsService service = new BbsService();
            Head head = null;
            try {
                // 指定されたkeyから記事を取得
                head = service.get(asKey("key"));
            }
            catch (Exception e) {
                // keyが不正な場合
            }
            // 記事が取得できなかった場合
            if (head == null) {
                // 指定されたキーに該当する記事がない場合はトップへ戻る
                errors.put("message", ApplicationMessage.get("error.entry.notfound"));
                return forward(basePath);
            }
            // コメントを作成
            Comment comment = new Comment();
            BeanUtil.copy(request, comment);
            comment.setPostDate(new Date());
            // 記事とコメントを登録
            service.insert(head, comment);
            // 記事詳細にリダイレクト(GETパラメータで記事の主キーを指定)
            return redirect(basePath + "read?key="+asString("key"));
        }
    
        private boolean validate() {
            Validators v = new Validators(request);
            v.add("username", v.required(),v.maxlength(50));
            v.add("comment", v.required());
            return v.validate();
        }
    }
    

    コメント登録処理も特に目新しい記述はありません。

  • テストケースの実行PostCommentControllerの実装が完了したらテストを実行し、結果が全てグリーンになることを確認します。
    PostCommentControllerTest成功イメージ
    これで全てのControllerとJSPの実装が終わったのでブラウザから一通りの動作確認が行えるはずです。記事の登録、そしてコメント登録を試してみましょう。まず新規に記事を登録して…
    記事の投稿イメージ
    登録した記事のタイトルをクリックして…
    記事詳細へ遷移イメージ
    記事詳細ページからコメントを投稿してみます。
    記事詳細へ遷移イメージ
    以下のように投稿したコメントが表示されれば、コメント登録処理の動作確認も完了です。
    コメント登録成功イメージ
    お疲れ様でした。これでSimpleBBSの実装はすべて完了です!作成したソースは以下になります。

    • simplebbs.controller.bbs.PostCommentController.java
    • simplebbs.controller.bbs.PostCommentControllerTest.java

    次はSimpleBBSを本番環境にデプロイする手順を説明します。