かんがるーさんの日記

最近自分が興味をもったものを調べた時の手順等を書いています。今は Spring Boot をいじっています。

Spring Boot + npm + Geb で入力フォームを作ってテストする ( その42 )( Form.js のテストを Jest で書く2 )

概要

記事一覧はこちらです。

Spring Boot + npm + Geb で入力フォームを作ってテストする ( その41 )( IntelliJ IDEA で Javascript を debug する ) の続きです。

参照したサイト・書籍

  1. jQuery - Category: Event Object
    https://api.jquery.com/category/events/event-object/

目次

  1. class 属性を変更するメソッドのテストを書く
    1. resetValidation メソッド
    2. setSuccess メソッド
    3. setError メソッド
  2. Validation 用メソッドのテストを書く
    1. convertAndValidate メソッド
  3. IntelliJ IDEA の 2017.3 から coverage をエディタ上に表示できる
  4. 次回は。。。

手順

class 属性を変更するメソッドのテストを書く

resetValidation メソッド

以下のテストを書いて、

    describe("class 属性を変更するメソッドのテスト", () => {

        describe("resetValidation メソッドのテスト", () => {

            test("入力チェック成功時(has-success)は has-success が削除される", () => {
                document.body.innerHTML = `
                    <div class="form-group has-success" id="form-group-name">
                      <div class="control-label col-sm-2">
                        <label class="float-label">お名前</label>
                      </div>
                      <div class="col-sm-10">
                        <div class="row"><div class="col-sm-10">
                          <input type="text" name="lastname" id="lastname" class="form-control form-control-inline"
                                value="" placeholder="例)田中"/>
                        </div></div>
                        <div class="row hidden js-errmsg"><div class="col-sm-10">
                          <p class="form-control-static text-danger"><small>ここにエラーメッセージを表示します</small></p>
                        </div></div>
                      </div>
                    </div>
                `;

                let form = new Form([]);
                expect($("#form-group-name").hasClass("has-success")).toBe(true);
                expect($(".js-errmsg").hasClass("hidden")).toBe(true);
                form.resetValidation("#form-group-name");
                expect($("#form-group-name").hasClass("has-success")).toBe(false);
                expect($(".js-errmsg").hasClass("hidden")).toBe(true);
            });

            test("入力チェックエラー時(has-error)は has-error が削除されてエラーメッセージが非表示になる", () => {
                document.body.innerHTML = `
                    <div class="form-group has-error" id="form-group-name">
                      <div class="control-label col-sm-2">
                        <label class="float-label">お名前</label>
                      </div>
                      <div class="col-sm-10">
                        <div class="row"><div class="col-sm-10">
                          <input type="text" name="lastname" id="lastname" class="form-control form-control-inline"
                                value="" placeholder="例)田中"/>
                        </div></div>
                        <div class="row js-errmsg"><div class="col-sm-10">
                          <p class="form-control-static text-danger"><small>ここにエラーメッセージを表示します</small></p>
                        </div></div>
                      </div>
                    </div>
                `;

                let form = new Form([]);
                expect($("#form-group-name").hasClass("has-error")).toBe(true);
                expect($(".js-errmsg").hasClass("hidden")).toBe(false);
                form.resetValidation("#form-group-name");
                expect($("#form-group-name").hasClass("has-error")).toBe(false);
                expect($(".js-errmsg").hasClass("hidden")).toBe(true);
            });

        });

    });

実行するとテストは成功しました。

f:id:ksby:20171220012914p:plain

setSuccess メソッド

以下のテストを書いて、

        test("setSuccess メソッドのテスト", () => {
            document.body.innerHTML = `
                <div class="form-group" id="form-group-name">
                </div>
            `;

            let form = new Form([]);
            expect($("#form-group-name").hasClass("has-success")).toBe(false);
            form.setSuccess("#form-group-name");
            expect($("#form-group-name").hasClass("has-success")).toBe(true);
        });

実行するとテストは成功しました。

f:id:ksby:20171220014053p:plain

setError メソッド

以下のテストを書いて、

        test("setError メソッドのテスト", () => {
            document.body.innerHTML = `
                <div class="form-group" id="form-group-name">
                  <div class="control-label col-sm-2">
                    <label class="float-label">お名前</label>
                  </div>
                  <div class="col-sm-10">
                    <div class="row"><div class="col-sm-10">
                      <input type="text" name="lastname" id="lastname" class="form-control form-control-inline"
                            value="" placeholder="例)田中"/>
                    </div></div>
                    <div class="row hidden js-errmsg"><div class="col-sm-10">
                      <p class="form-control-static text-danger"><small>ここにエラーメッセージを表示します</small></p>
                    </div></div>
                  </div>
                </div>
            `;

            let form = new Form([]);
            expect($("#form-group-name").hasClass("has-error")).toBe(false);
            expect($(".js-errmsg").hasClass("hidden")).toBe(true);
            expect($(".js-errmsg small").text()).toBe("ここにエラーメッセージを表示します");
            form.setError("#form-group-name", "お名前を入力してください");
            expect($("#form-group-name").hasClass("has-error")).toBe(true);
            expect($(".js-errmsg").hasClass("hidden")).toBe(false);
            expect($(".js-errmsg small").text()).toBe("お名前を入力してください");
        });

実行するとテストは成功しました。

f:id:ksby:20171220014840p:plain

Validation 用メソッドのテストを書く

convertAndValidate メソッド

以下のテストを書いて、

    describe("Validation 用メソッドのテスト", () => {

        describe("convertAndValidate メソッドのテスト", () => {

            const idFormGroup = "#form-group-name";
            const idList = ["#name", "#age"];

            test("全ての要素に focus していると converter, validator が呼ばれる", () => {
                let form = new Form(idList);
                let event = $.Event("test");
                const converter = jest.fn();
                const validator = jest.fn();

                form.forceAllFocused(form);
                form.convertAndValidate(form, event, idFormGroup, idList, converter, validator);
                expect(converter).toBeCalled();
                expect(validator).toBeCalled();
                expect(event.isPropagationStopped()).toBe(false);
            });

            test("全ての要素に focus していないと converter, validator は呼ばれない", () => {
                let form = new Form(idList);
                let event = $.Event("test");
                const converter = jest.fn();
                const validator = jest.fn();

                form.convertAndValidate(form, event, idFormGroup, idList, converter, validator);
                expect(converter).not.toBeCalled();
                expect(validator).not.toBeCalled();
                expect(event.isPropagationStopped()).toBe(false);
            });

            test("converter, validator に undefined を渡してもエラーにならない", () => {
                let form = new Form(idList);
                let event = $.Event("test");

                form.forceAllFocused(form);
                expect(() => {
                    form.convertAndValidate(form, event, idFormGroup, idList, undefined, undefined);
                }).not.toThrow();
                expect(event.isPropagationStopped()).toBe(false);
            });

            test("validator が Error オブジェクトを throw すると event.stopPropagation() が呼ばれる", () => {
                let form = new Form(idList);
                let event = $.Event("test");
                const converter = jest.fn();
                const validatorThrowError = jest.fn().mockImplementation(() => {
                    throw new Error();
                });

                form.forceAllFocused(form);
                form.convertAndValidate(form, event, idFormGroup, idList, converter, validatorThrowError);
                expect(event.isPropagationStopped()).toBe(true);
            });

        });

    });

実行するとテストは成功しました。

f:id:ksby:20171221011404p:plain

IntelliJ IDEA の 2017.3 から coverage をエディタ上に表示できる

IntelliJ IDEA 2017.3 から Jest のテストを実行するコンテキストメニューに「Run ... with Coverage」が表示されるようになりました。また、このメニューからテストを実行すると Jest のレポートファイルを見なくてもエディタ上で実行された部分が分かります。

例えば「Run 'focus イベントに関するメソッドのテスト' with Coverage」を実行してみると、

f:id:ksby:20171222060252p:plain

テストが実行されて成功し、

f:id:ksby:20171222060938p:plain

Coverage の Window が表示されて、Form.js の 39% の行がテストされたことが分かります。

f:id:ksby:20171222061130p:plain f:id:ksby:20171222061300p:plain

Form.js をエディタで開いてみると、画面の左側にテストが実行された箇所が緑で、実行されていない箇所が赤で表示されます。

f:id:ksby:20171222061527p:plain

テストの実行された/されていない箇所を表示する色はデフォルトでは見にくい色だったので、単純な緑、赤に変更しています。色が表示されている部分にマウスカーソルを移動して左クリックした後、「Edit coverage colors」のアイコンをクリックします。

f:id:ksby:20171222061804p:plain

「Editor | Color Scheme | General」ダイアログが表示されるので、緑は 00FF00 へ、

f:id:ksby:20171222062055p:plain f:id:ksby:20171222062137p:plain

赤は FF0000 へ変更しています。

f:id:ksby:20171222062319p:plain f:id:ksby:20171222062410p:plain

次回は。。。

もう少し Jest でテストを書いてみます。jQuery.ajax 呼び出し時の非同期処理や、setTimeout で遅延して処理を実行する場合のテストを書く予定です。

それにしても IntelliJ IDEA 2017.3 + Jest の環境だと、Javascript のテストを書いたり実行したり、coverage を確認したり、うまく動かない時に debug 実行したりも、Java の時とたいして変わりがないですね。開発しやすい印象です。テスト実行時の Node.js のパラメータ指定(--inspect 等)も IntelliJ IDEA が自動でセットしてくれるのは便利です。

履歴

2017/12/22
初版発行。