Spring Boot + npm + Geb で入力フォームを作ってテストする ( その40 )( Form.js のテストを Jest で書く )
概要
記事一覧はこちらです。
Spring Boot + npm + Geb で入力フォームを作ってテストする ( その39 )( Spring Boot を 1.5.7 → 1.5.9 へバージョンアップする ) の続きです。
- 今回の手順で確認できるのは以下の内容です。
- Jest を使用して Javascript のモジュールのテストを書きます。
- 今回は src/main/assets/js/lib/class/Form.js のテストを書きます。
参照したサイト・書籍
How to duplicate object properties in another object?
https://stackoverflow.com/questions/9362716/how-to-duplicate-object-properties-in-another-objectObject.keys()
https://developer.mozilla.org/ja/docs/Web/JavaScript/Reference/Global_Objects/Object/keys
目次
手順
テストを書くファイルを用意する
src/test/assets/tests/lib の下に class ディレクトリを作成し、その下に Form.test.js を新規作成して以下の内容を記述します。
"use strict"; global.$ = require("jquery"); const Form = require("lib/class/Form.js"); describe("Form.js のテスト", () => { describe("focus イベントに関するメソッドのテスト", () => { beforeEach(() => { document.body.innerHTML = ` <input type="text" name="name" id="name" value=""/> <input type="radio" name="age" id="age_1" value="1"/>男性 <input type="radio" name="age" id="age_2" value="2"/>女性 <input type="checkbox" name="enquete" id="enquete_1" value="1"/>回答1 <input type="checkbox" name="enquete" id="enquete_2" value="2"/>回答2 <select id="job"> <option value="1">会社員</option> <option value="2">学生</option> <option value="3">その他</option> </select> `; }); const idList = [ "#name", "input:radio[name='age']", "input:checkbox[name='enquete']", "#job" ]; // ここにテストを書く }); });
focus イベントに関するメソッドのテストを書く
setFocused メソッド
以下のテストを書いて、
test("focusイベントが発生するとfocusedプロパティにselectorがセットされる", () => { let form = new Form(idList); expect(form.focused).not.toHaveProperty("#name", true); expect(form.focused).not.toHaveProperty("input:radio[name='age']", true); expect(form.focused).not.toHaveProperty("input:checkbox[name='enquete']", true); expect(form.focused).not.toHaveProperty("#job", true); $("#name").focus(); expect(form.focused).toHaveProperty("#name", true); expect(form.focused).not.toHaveProperty("input:radio[name='age']", true); expect(form.focused).not.toHaveProperty("input:checkbox[name='enquete']", true); expect(form.focused).not.toHaveProperty("#job", true); $("input:radio[name='age']").focus(); expect(form.focused).toHaveProperty("#name", true); expect(form.focused).toHaveProperty("input:radio[name='age']", true); expect(form.focused).not.toHaveProperty("input:checkbox[name='enquete']", true); expect(form.focused).not.toHaveProperty("#job", true); $("input:checkbox[name='enquete']").focus(); expect(form.focused).toHaveProperty("#name", true); expect(form.focused).toHaveProperty("input:radio[name='age']", true); expect(form.focused).toHaveProperty("input:checkbox[name='enquete']", true); expect(form.focused).not.toHaveProperty("#job", true); $("#job").focus(); expect(form.focused).toHaveProperty("#name", true); expect(form.focused).toHaveProperty("input:radio[name='age']", true); expect(form.focused).toHaveProperty("input:checkbox[name='enquete']", true); expect(form.focused).toHaveProperty("#job", true); });
実行するとテストは成功しました。
isAllFocused メソッド
以下のテストを書いて、
test("isAllFocused メソッドのテスト", () => { const idList2 = [ "input:radio[name='age']", "input:checkbox[name='enquete']" ]; let form = new Form(idList); expect(form.isAllFocused(form, idList)).toBe(false); $("#name").focus(); expect(form.isAllFocused(form, idList)).toBe(false); $("input:radio[name='age']").focus(); expect(form.isAllFocused(form, idList)).toBe(false); expect(form.isAllFocused(form, idList2)).toBe(false); $("input:checkbox[name='enquete']").focus(); expect(form.isAllFocused(form, idList)).toBe(false); expect(form.isAllFocused(form, idList2)).toBe(true); $("#job").focus(); expect(form.isAllFocused(form, idList)).toBe(true); });
実行するとテストは成功しました。
setFocusedFromList メソッド
以下のテストを書いて、
test("setFocusedFromList メソッドのテスト", () => { let form = new Form(idList); expect(form.isAllFocused(form, idList)).toBe(false); form.setFocusedFromList(form, idList); expect(form.isAllFocused(form, idList)).toBe(true); const idList2 = [ "input:radio[name='age']", "input:checkbox[name='enquete']" ]; let form2 = new Form(idList); expect(form2.isAllFocused(form2, idList)).toBe(false); form2.setFocusedFromList(form2, idList2); expect(form2.isAllFocused(form2, idList)).toBe(false); expect(form2.isAllFocused(form2, idList2)).toBe(true); });
実行するとテストは成功しました。
forceAllFocused メソッド
以下のテストを書いて、
test("forceAllFocused メソッドのテスト", () => { let form = new Form(idList); expect(form.isAllFocused(form, idList)).toBe(false); form.forceAllFocused(form); expect(form.isAllFocused(form, idList)).toBe(true); });
実行するとテストは成功しました。
backupFocusedState, restoreFocusedState メソッド
以下のテストを書いて、
test("backupFocusedState, restoreFocusedState メソッドのテスト", () => { let form = new Form(idList); form.forceAllFocused(form); expect(form.focused).toHaveProperty("#name", true); expect(form.focused).toHaveProperty("input:radio[name='age']", true); expect(form.focused).toHaveProperty("input:checkbox[name='enquete']", true); expect(form.focused).toHaveProperty("#job", true); expect(form.backupFocused).not.toHaveProperty("#name", true); expect(form.backupFocused).not.toHaveProperty("input:radio[name='age']", true); expect(form.backupFocused).not.toHaveProperty("input:checkbox[name='enquete']", true); expect(form.backupFocused).not.toHaveProperty("#job", true); form.backupFocusedState(form); expect(form.backupFocused).toHaveProperty("#name", true); expect(form.backupFocused).toHaveProperty("input:radio[name='age']", true); expect(form.backupFocused).toHaveProperty("input:checkbox[name='enquete']", true); expect(form.backupFocused).toHaveProperty("#job", true); form.focused = {}; form.restoreFocusedState(form); expect(form.focused).toHaveProperty("#name", true); expect(form.focused).toHaveProperty("input:radio[name='age']", true); expect(form.focused).toHaveProperty("input:checkbox[name='enquete']", true); expect(form.focused).toHaveProperty("#job", true); });
実行するとテストが失敗しました。。。
いろいろ調べて How to duplicate object properties in another object? や Object.keys() の記事を見つけました。オブジェクトのプロパティをコピーするのに配列のコピーと勘違いしていたからでした。。。 Form.js を以下のように変更します。
function Form(idList) { this.idList = idList; this.focused = {}; this.backupFocused = {}; addFocusEventListener(this); } .......... /** * form.focused の値をバックアップする * @param {Form} form - Form オブジェクト */ Form.prototype.backupFocusedState = function (form) { Object.keys(form.focused).forEach(function (key) { form.backupFocused[key] = form.focused[key]; }); }; /** * form.focused の値を form.backupFocusedState を呼び出した時の状態に戻す * @param {Form} form - Form オブジェクト */ Form.prototype.restoreFocusedState = function (form) { Object.keys(form.backupFocused).forEach(function (key) { form.focused[key] = form.backupFocused[key]; }); };
- focused, backupFocused のプロパティの初期化を
[]
→{}
に変更します。 - backupFocusedState, restoreFocusedState メソッド内の処理を
Object.keys()
を使用する方法に変更します。
再度テストを実行すると今度は成功しました。
これまで作成したテストを全て実行しても成功しました。
値の入力有無をチェックするメソッドのテストを書く
isAllEmpty メソッド
以下のテストを書いて、
describe("値の入力有無をチェックするメソッドのテストを書く", () => { beforeEach(() => { document.body.innerHTML = ` <input type="text" name="name" id="name" value=""/> <input type="radio" name="age" id="age_1" value="1"/>男性 <input type="radio" name="age" id="age_2" value="2"/>女性 <input type="checkbox" name="enquete" id="enquete_1" value="1"/>回答1 <input type="checkbox" name="enquete" id="enquete_2" value="2"/>回答2 <select id="job"> <option value="">選択してください</option> <option value="1">会社員</option> <option value="2">学生</option> <option value="3">その他</option> </select> `; }); const idList = [ "#name", "input:radio[name='age']", "input:checkbox[name='enquete']", "#job" ]; test("isAllEmpty メソッドのテスト", () => { let form = new Form(idList); expect(form.isAllEmpty(idList)).toBe(true); // input[type='text'] $("#name").val("a"); expect(form.isAllEmpty(idList)).toBe(false); $("#name").val(""); expect(form.isAllEmpty(idList)).toBe(true); // input[type='radio'] $("input:radio[name='age'][value='1']").prop("checked", true); expect(form.isAllEmpty(idList)).toBe(false); $("input:radio[name='age'][value='1']").prop("checked", false); expect(form.isAllEmpty(idList)).toBe(true); $("input:radio[name='age'][value='2']").prop("checked", true); expect(form.isAllEmpty(idList)).toBe(false); $("input:radio[name='age'][value='2']").prop("checked", false); expect(form.isAllEmpty(idList)).toBe(true); // input[type='checkbox'] $("input:checkbox[name='enquete'][value='1']").prop("checked", true); expect(form.isAllEmpty(idList)).toBe(false); $("input:checkbox[name='enquete'][value='1']").prop("checked", false); expect(form.isAllEmpty(idList)).toBe(true); $("input:checkbox[name='enquete'][value='2']").prop("checked", true); expect(form.isAllEmpty(idList)).toBe(false); $("input:checkbox[name='enquete'][value='2']").prop("checked", false); expect(form.isAllEmpty(idList)).toBe(true); $("input:checkbox[name='enquete'][value='1']").prop("checked", true); $("input:checkbox[name='enquete'][value='2']").prop("checked", true); expect(form.isAllEmpty(idList)).toBe(false); $("input:checkbox[name='enquete'][value='1']").prop("checked", false); $("input:checkbox[name='enquete'][value='2']").prop("checked", false); expect(form.isAllEmpty(idList)).toBe(true); // select $("#job").val("1"); expect(form.isAllEmpty(idList)).toBe(false); $("#job").val("2"); expect(form.isAllEmpty(idList)).toBe(false); $("#job").val("3"); expect(form.isAllEmpty(idList)).toBe(false); $("#job").val(""); expect(form.isAllEmpty(idList)).toBe(true); }); });
実行するとテストが失敗しました。最初の expect(form.isAllEmpty(idList)).toBe(true);
で失敗していますが、checkbox の処理を実装していないので、当然ですね。。。
Form.js を以下のように変更します。
/** * idList で渡された id の要素が全て未入力/未選択かチェックする * @param {Array} idList - チェックする id がセットされた配列 * @returns {boolean} true:全て未入力/未選択である, false:1つ以上入力/選択されているものがある */ Form.prototype.isAllEmpty = function (idList) { var allEmpty = true; idList.forEach(function (id) { if ($(id).attr("type") === "radio") { if ($(id + ":checked").val() !== undefined) { allEmpty = false; } } else if ($(id).attr("type") === "checkbox") { if ($(id + ":checked").length > 0) { allEmpty = false; } } else if ($(id).val() !== "") { allEmpty = false; } }); return allEmpty; };
else if ($(id).attr("type") === "checkbox") { ... }
の処理を追加します。
再度テストを実行すると今度は成功しました。
isAnyEmpty メソッド
isAnyEmpty メソッドも checkbox の実装をしていないので、テストの前に修正します。Form.js を以下のように変更します。
/** * idList で渡された id の要素の中に未入力/未選択のものがあるかチェックする * @param {Array} idList - チェックする id がセットされた配列 * @returns {boolean} true:未入力/未選択のものがある, false:全て入力/選択されている */ Form.prototype.isAnyEmpty = function (idList) { var anyEmpty = false; idList.forEach(function (id) { if ($(id).attr("type") === "radio") { if ($(id + ":checked").val() === undefined) { anyEmpty = true; } } else if ($(id).attr("type") === "checkbox") { if ($(id + ":checked").length === 0) { anyEmpty = true; } } else if ($(id).val() === "") { anyEmpty = true; } }); return anyEmpty; };
else if ($(id).attr("type") === "checkbox") { ... }
を追加します。
以下のテストを書いて、
test("isAnyEmpty メソッドのテスト", () => { let form = new Form(idList); expect(form.isAnyEmpty(idList)).toBe(true); $("#name").val("a"); $("input:radio[name='age'][value='1']").prop("checked", true); $("input:checkbox[name='enquete'][value='1']").prop("checked", true); $("#job").val("1"); expect(form.isAnyEmpty(idList)).toBe(false); $("#name").val(""); expect(form.isAnyEmpty(idList)).toBe(true); $("#name").val("a"); expect(form.isAnyEmpty(idList)).toBe(false); $("input:radio[name='age'][value='1']").prop("checked", false); expect(form.isAnyEmpty(idList)).toBe(true); $("input:radio[name='age'][value='1']").prop("checked", true); expect(form.isAnyEmpty(idList)).toBe(false); $("input:checkbox[name='enquete'][value='1']").prop("checked", false); expect(form.isAnyEmpty(idList)).toBe(true); $("input:checkbox[name='enquete'][value='1']").prop("checked", true); expect(form.isAnyEmpty(idList)).toBe(false); $("input:checkbox[name='enquete'][value='2']").prop("checked", true); expect(form.isAnyEmpty(idList)).toBe(false); $("input:checkbox[name='enquete'][value='1']").prop("checked", false); expect(form.isAnyEmpty(idList)).toBe(false); $("input:checkbox[name='enquete'][value='2']").prop("checked", false); expect(form.isAnyEmpty(idList)).toBe(true); $("input:checkbox[name='enquete'][value='1']").prop("checked", true); $("#job").val(""); expect(form.isAnyEmpty(idList)).toBe(true); $("#job").val("1"); expect(form.isAnyEmpty(idList)).toBe(false); });
実行するとテストは成功しました。
isAnyNotEmpty メソッド
isAnyNotEmpty メソッドも checkbox の実装をしていないので、テストの前に修正します。Form.js を以下のように変更します。
/** * idList で渡された id の要素の中に1つでも入力/選択されているものがあるかチェックする * @param {Array} idList - チェックする id がセットされた配列 * @returns {boolean} true:1つでも入力/選択されている, false:全て未入力/未選択である */ Form.prototype.isAnyNotEmpty = function (idList) { var anyNotEmpty = false; idList.forEach(function (id) { if ($(id).attr("type") === "radio") { if ($(id + ":checked").val() !== undefined) { console.log(id + ", " + $(id + ":checked").val()); anyNotEmpty = true; } } else if ($(id).attr("type") === "checkbox") { if ($(id + ":checked").length > 0) { console.log(id + ", " + $(id + ":checked").length); anyNotEmpty = true; } } else if ($(id).val() !== "") { console.log(id + ", " + $(id).val()); anyNotEmpty = true; } }); return anyNotEmpty; };
else if ($(id).attr("type") === "radio") { ... }
を追加します。
以下のテストを書いて、
test("isAnyNotEmpty メソッドのテスト", () => { let form = new Form(idList); expect(form.isAnyNotEmpty(idList)).toBe(false); // input[type='text'] $("#name").val("a"); expect(form.isAnyNotEmpty(idList)).toBe(true); $("#name").val(""); expect(form.isAnyNotEmpty(idList)).toBe(false); $("input:radio[name='age'][value='1']").prop("checked", true); expect(form.isAnyNotEmpty(idList)).toBe(true); $("input:radio[name='age'][value='1']").prop("checked", false); expect(form.isAnyNotEmpty(idList)).toBe(false); $("input:checkbox[name='enquete'][value='1']").prop("checked", true); expect(form.isAnyNotEmpty(idList)).toBe(true); $("input:checkbox[name='enquete'][value='1']").prop("checked", false); expect(form.isAnyNotEmpty(idList)).toBe(false); $("input:checkbox[name='enquete'][value='2']").prop("checked", true); expect(form.isAnyNotEmpty(idList)).toBe(true); $("input:checkbox[name='enquete'][value='2']").prop("checked", false); expect(form.isAnyNotEmpty(idList)).toBe(false); $("input:checkbox[name='enquete'][value='1']").prop("checked", true); $("input:checkbox[name='enquete'][value='2']").prop("checked", true); expect(form.isAnyNotEmpty(idList)).toBe(true); $("input:checkbox[name='enquete'][value='1']").prop("checked", false); $("input:checkbox[name='enquete'][value='2']").prop("checked", false); expect(form.isAnyNotEmpty(idList)).toBe(false); $("#job").val("1"); expect(form.isAnyNotEmpty(idList)).toBe(true); $("#job").val(""); expect(form.isAnyNotEmpty(idList)).toBe(false); });
実行するとテストは成功しました。
ここまで作成したテストが全て成功することを確認します。
続きます。。。
Jest + IntelliJ IDEA を組み合わると jQuery を利用した Javascript のモジュールのテストが書きやすいです。document.body.innerHTML
に必要最低限の HTML だけ記述して動作確認が出来るのが便利ですね。
IntelliJ IDEA を 2017.3.1 へバージョンアップしてから、Form.js のテストを続けます。
履歴
2017/12/16
初版発行。