Lesson 11
ReactとFirebaseでTODOアプリの作成 - テストの実施
Lesson 11
Chapter 1
Jest
テストの必要性
プログラムの正しい動作を検証する方法として、コードのテストがあります。テストを行うことで以下のようなメリットが得られます。
- バグの早期発見: テストを書くことで、アプリケーションが意図した通りに動作するかどうかを確認でき、バグを早期発見が可能になります。
- リファクタリング: テストがあることで、コードのリファクタリングを行っても、アプリケーションが正常に動作することを確認できます。
- 信頼性の向上: テストを書くことにより、コードの品質が向上し、信頼性を高めることができます。
Reactアプリケーションのテストを行うための代表的なフレームワークにJestがあります。
Jestとは
JestはMeta社(旧Facebook社)によって開発されたJavaScriptのテスティングフレームワークです。元々はReactプロジェクトのために作られたましたが、AngularやVueのプロジェクトでも使用することが可能です。Jestの導入により、JavaScriptやTypeScriptで書かれたほとんどのコードをテストすることができます。
Jest環境の導入
create-react-appで作成したReactプロジェクトに対してJestを使用する場合、パッケージにあらかじめ含まれているため、あらためてインストールする必要ありません。代わりに後ほど実施するスナップショットテストのために、react-test-rendererを以下のコマンドでインストールしましょう。
npm install --save-dev react-test-renderer
react-test-rendererの使用により、ブラウザなしでReactコンポーネントからDOMツリーをレンダリング可能となります。スナップショットテストでは、DOMツリーのスナップショットを作成してテストを行います。
Jestの書き方
Jestのテストコードの書き方は以下になります。
test(name, fn);
testの引数"name"はテスト名を表す文字列、"fn"はテストの確認項目を含む関数を設定します。テストに使用する関数の詳細は公式サイト内の説明をご確認ください。テストに使用するファイル名を"test_a"とする場合、必ず"test_a.test.js"という名前にします。Jestに使用するファイルは任意のファイル名の後に、".test.js"を付けるルールになっています。
簡単な例でtestの使い方を確認してみます。1と2の和が3であるかどうかをテストする以下のコードを記述し、任意のプロジェクトのsrcフォルダにaddition.test.jsで保存しましょう。
addition.test.js
test('check_of_addition', () => {
expect(1+2).toBe(3);
});
ターミナルを起動し、プロジェクトフォルダに移動して以下を実行します。
npm run test -- src/addition.test.js
"npm run test -- . . . "の" . . . "に相対パスでファイル名を指定すると指定したファイルのテストができます。実行すると、以下の結果が表示されます。
PASS src/addition.test.js
√ check_of_addition (2 ms)
Test Suites: 1 passed, 1 total
Tests: 1 passed, 1 total
Snapshots: 0 total
Time: 3.418 s
Ran all test suites matching /src\\addition.test.js/i.
"1 passed, 1 total"と表示され1件のテストがパスしたことが確認できます。以上は結果が正しい場合の表示です。テスト結果の最後に"Watch Usage"と表示されその下にキーと対応する操作が表示されます。終了する場合、"q"を押します。あえて正しくない結果をテストして、パスしない場合についても見てみましょう。addition.test.jsの"toBe(3)"を"toBe(0)"に変更すると、以下の結果が表示されます。
FAIL src/addition.test.js
× check_of_addition (5 ms)
● check_of_addition
expect(received).toBe(expected) // Object.is equality
Expected: 0
Received: 3
1 | test('check_of_addition', () => {
> 2 | expect(1+2).toBe(0);
| ^
3 | });
at Object. (src/addition.test.js:2:17)
Test Suites: 1 failed, 1 total
Tests: 1 failed, 1 total
Snapshots: 0 total
Time: 3.546 s
Ran all test suites matching /src\\addition.test.js/i.
"1 failed, 1 total"と表示され1件のテストがパスしなかったことが確認できます。また、コードのどのテストが失敗したかが示されています。以上が結果が正しくない場合の表示です。いくつかの関連するテストをまとめて実施する場合、以下に示す"describe"を使用します。
describe(name, () => {
test( . . . );
test( . . . );
・
・
・
(省略)
・
・
・
test( . . . );
});
"name"はtestと同様にタイトルになります。上記のように、第二引数にtestを複数設定することができます。

Lesson 11
Chapter 2
テストコードの実装
スナップショットテストについて
アプリのアップデート等でコードに変更を加えた時に、変更前後で表示に変化がないかを確認できる方法として、スナップショットテストがあります。Reactでよく使用されるテストの方法になります。今後自分で作成したアプリに対してスナップショットテストを実行するための練習として、今回実装したTODOの各画面に対してスナップショットテストを実行していきます。その前に、簡単な例でスナップショットテストの方法を確認してみたいと思います。以下のコードを作成し、Test.test.jsという名前でTODOアプリのプロジェクトのsrcフォルダに保存しましょう。
Test.test.js
import renderer from 'react-test-renderer'
import { Test } from './Test';
describe('snapshot-test', () => {
test('test of snapshot test', () => {
const tree = renderer.create(<Test />).toJSON();
expect(tree).toMatchSnapshot();
});
});
上記がスナップショットテストを実行するためのコードになります。上記のコンポーネント名である"Test"の部分をテストするコンポーネント名に置き換えることで置き換えたコンポーネントでのテストが可能になります。続いて、Testコンポーネントを作成します。テストのやり方を説明するためのコードなのでシンプルに以下のように記述しましょう。記述出来たら、Test.jsxというファイル名で同じくsrcフォルダに保存しましょう。
Test.jsx
const Test = () => {
return (
<p>こんにちは!</p>
);
}
export { Test }
以下のコマンドで実行してみましょう。
npm run test -- src/Test.test.js
実行するとターミナルに以下の内容が表示されます。
PASS src/Test.test.js
snapshot-test
√ test of snapshot test (13 ms)
› 1 snapshot written.
Snapshot Summary
› 1 snapshot written from 1 test suite.
Test Suites: 1 passed, 1 total
Tests: 1 passed, 1 total
Snapshots: 1 written, 1 total
Time: 1.388 s
Ran all test suites matching /src\\Test.test.js/i.
英語ですが、"1枚のスナップショットが作成された"という意味の表示がされています。実行後、srcフォルダの中に、__snapshots__フォルダが作成され、その中に"Test.test.js.snap"というファイルが作られます。内容を確認すると、以下になります。
// Jest Snapshot v1, https://goo.gl/fbAQLP
exports[`snapshot-test test of snapshot test 1`] = `
<p>
こんにちは!
</p>
`;
レンダリング結果が内容に含まれていることが確認できます。再度テストを実行すると、ターミナルに以下の内容が表示されます。
PASS src/Test.test.js
snapshot-test
√ test of snapshot test (11 ms)
Test Suites: 1 passed, 1 total
Tests: 1 passed, 1 total
Snapshots: 1 passed, 1 total
Time: 3.536 s
Ran all test suites matching /src\\Test.test.js/i.
今度はスナップショットが作成されたというメッセージは表示されず、テストスイート、テスト、スナップショットがパスされていること確認できます。これがスナップショットテストにパスした状態となります。レンダリング内容が変更された場合の結果を確認するために、Test.jsxを以下のように書き換えてみましょう。
Test.jsx
const Test = () => {
return (
<p>こんばんは!</p>
);
}
export { Test }
pタグによる表示内容が"こんにちは!"から"こんばんは!"に変わりました。テストを実行すると、ターミナルに以下の内容が表示されます。
FAIL src/Test.test.js
snapshot-test
× test of snapshot test (18 ms)
● snapshot-test › test of snapshot test
expect(received).toMatchSnapshot()
Snapshot name: `snapshot-test test of snapshot test 1`
- Snapshot - 1
+ Received + 1
<p>
- こんにちは!
+ こんばんは!
</p>
5 | test('test of snapshot test', () => {
6 | const tree = renderer.create(<Test />).toJSON();
> 7 | expect(tree).toMatchSnapshot();
| ^
8 | });
9 | });
at Object. (src/Test.test.js:7:18)
› 1 snapshot failed.
Snapshot Summary
› 1 snapshot failed from 1 test suite. Inspect your code changes or press `u` to update them.
Test Suites: 1 failed, 1 total
Tests: 1 failed, 1 total
Snapshots: 1 failed, 1 total
Time: 4.788 s
Ran all test suites matching /src\\Test.test.js/i.
テストが失敗し、"こんにちは!"が"こんばんは!"に変更されたことが分かる内容が表示されています。これがスナップショットテストに失敗した状態となります。テスト結果の最後に"Watch Usage"と表示されている状態で、"q"のキーを押すと終了しますが、"u"のキーを押すとスナップショットは上書きされます。変更後の結果を以降のテストで採用するときは上書きすると良いです。このように、スナップショットテストにより、コンポーネントのレンダリング結果を保存でき、更新後に変化を調べることができるようになります。
事前準備
ここからは前のレッスンで作成したTODOアプリの各画面にスナップショットテストを実行していきます。その準備として、ToDo.jsx、Edit.jsx、List.jsxApp.jsxを一部変更したToDoForTest.jsx、EditForTest.jsx、ListForTest.jsxを以下に示すように作成し、元のファイルと同じフォルダに保存します。
ToDoForTest.jsx
import { useState,memo } from "react"
import { Outlet, useNavigate, Navigate } from 'react-router-dom';
import { useContext } from "react"
import { UserInfoContext } from "../context/UserInfoProvider";
import { header, modeChangeButton, logoutButton } from "../styles";
const ToDo = memo(({user, editing}) => { //Propsにuserとeditingを設定
// const [editing, setEditing] = useState(false); コメント化
// const { user } = useContext(UserInfoContext); コメント化
const navigate = useNavigate();
const changeMode = () => {
navigate(editing?"/to_do":"edit");
setEditing(!editing);
}
const toSignInPage = () => {
navigate("/signin");
};
return (
!user
?<Navigate to="/signin" replace/>
:(
<div>
<div style={header}>
<button onClick = {changeMode} style={modeChangeButton}>{editing?"表示":"編集"}</button>
<button onClick = {toSignInPage} style={logoutButton}>サインアウト</button>
</div>
<fieldset>
<legend>{user.displayName}さんのTo Do</legend>
<Outlet />
</fieldset>
</div>
)
);
});
export { ToDo };
EditForTest.jsx
import { useEffect, memo } from "react";
import { toDoStyle, listStyle, editButton, textbox, additionButton } from "../styles";
import { useToDos } from "../hooks/useToDos";
const Edit = memo (({loadedCollection}) => { //PropsにloadedCollectionを設定
//const { isUpdated, loadedCollection, getToDos, editToDo, editingId, setEditedToDo, additingToDo, setAddedToDo, addToDo } = useToDos(); コメント化
const { isUpdated, getToDos, editToDo, editingId, setEditedToDo, additingToDo, setAddedToDo, addToDo } = useToDos(); //戻り値からloadedCollectionを削除
const onClickEdit = (id, task) => editToDo(id, task);
const onChangeText = (event) => setEditedToDo( event.target.value );
const onChangeAdditionalText = (event) => setAddedToDo( event.target.value );
const onClickAdd = () => addToDo();
useEffect(()=>{getToDos()}, [isUpdated]);
return (
<div style = {listStyle}>
<ul>
{loadedCollection.map((document) => (
document.task&& (
<li key = {document.id}>
<div style = {toDoStyle}>
{editingId === document.id?<input type="text" defaultValue={document.task} onChange={event=>onChangeText(event)} style={textbox}/>:<label >{document.task}</label>}
<button onClick={()=>onClickEdit(document.id, document.task)} style = {editButton}>{editingId === document.id?"完了":"編集"}</button>
</div>
</li>
)
))
}
</ul>
{additingToDo&&<input type="text" onChange={event=>onChangeAdditionalText(event)} style={textbox}/>}
<button onClick={onClickAdd} style={additingToDo?{}:additionButton}>{additingToDo?"完了":"新規登録"}</button>
</div>
);
});
export { Edit };
ListForTest.jsx
import { useEffect, memo } from "react";
import { toDoStyle, listStyle, editButton } from "../styles";
import { useToDos } from "../hooks/useToDos";
const List = memo(({loadedCollection}) => { //PropsにloadedCollectionを設定
//const {isUpdated, loadedCollection, getToDos, deleteToDo, checkToDo } = useToDos(); コメント化
const {isUpdated, getToDos, deleteToDo, checkToDo } = useToDos(); //戻り値からloadedCollectionを削除
const onClickDelete = id => deleteToDo(id);
const onChangeCheckBox = (event,id) => checkToDo(event,id);
useEffect(()=>{getToDos()}, [isUpdated]);
return (
<div style={listStyle}>
<ul>
{loadedCollection.map((document) => (
document.task&&(
<li key = {document.id}>
<div style={toDoStyle}>
<label style={{textDecoration : document.finished?"line-through":"none"}} >
{document.task}
<input type="checkbox" onChange={(event)=>{onChangeCheckBox(event,document.id)}} checked = {document.finished}/>
</label>
<button onClick={()=>onClickDelete(document.id)} style = {editButton}>削除</button>
</div>
</li>)
))
}
</ul>
</div>
)
});
export { List };
変更箇所にはコメントを記しています。なぜこれらのファイルを変更したかを説明します。ToDoForTest.jsxにおいては、ユーザー情報を持つ変数"user"が条件文においてtrueと判定されないと、サインイン画面にリダイレクトされるようになっているため、テスト時にデータを渡せるように、useContextからではなく外部からPropsとして取得できるようにしています。また、変数"editing"によって、TODO登録・編集画面とTODO一覧画面を切り替えるボタンの表示が変更されるので、これも外部からPropsとして取得できるようにしています。EditForTest.jsxとListForTest.jsxにおいては、データベースのサブコレクション"TODO"の内容を格納する"loadedCollection"にテスト用の仮の値を代入できるように、外部からPropsとして取得するように変更しています。
また今回、サインイン画面、TODO登録・編集画面、TODO一覧画面の3つの画面のスナップショットテストを個別のコンポーネントファイルではなく、App.jsxを使用して実施します。対象とするコンポーネントを指定できるように書き換えたApp.jsxを以下のように作成し、AppForTest.jsxとして元のファイルと同じフォルダに保存します。
AppForTest.jsx
import './App.css';
import { memo } from "react";
import { SignIn } from './pages/SignIn'
import { MemoryRouter, Routes, Route, Navigate } from "react-router-dom"; //BrowserRouter→MemoryRouterに変更
import { ToDo } from './pages/ToDoForTest'; //fromが指定するファイルを'./pages/ToDo'→'./pages/ToDoForTest'に変更
import { List } from './components/ListForTest';
import { Edit } from './components/EditForTest';
const App = memo(({url, loadedCollection, editing}) => { //Propsにurl、loadedCollection、editingを設定
return (
<div className="App">
<MemoryRouter initialEntries={[url]}> {/*BrowserRouter→MemoryRouterに変更し、PropsとしてinitialEntries={[url]}を設定*/}
<Routes>
<Route path="/" element={ <Navigate to="/signin" replace/>} />
<Route path="/signin" element={ <SignIn />} />
<Route path="/to_do" element={ <ToDo user={{displayName:"GEST"}} editing={editing} />} > {/*Propsとしてuser={{displayName:"GEST"}}とediting={editing}を設定*/}
<Route index element={ <List loadedCollection={loadedCollection}/> } /> {/*PropsとしてloadedCollection={loadedCollection}を設定*/}
<Route path="edit" element={ <Edit loadedCollection={loadedCollection}/> } /> {/*PropsとしてloadedCollection={loadedCollection}を設定*/}
</Route>
<Route path="*" element={<Navigate to='/signin' replace />} />
</Routes>
</MemoryRouter> {/*BrowserRouter→MemoryRouterに変更*/}
</div>
);
});
export { App };
変更箇所にはコメントを記しています。AppForTest.jsxにおいてはスナップショットテストの対象となるコンポーネントを指定する変数"url"、テスト用のデータベースの配列"loadedCollection"、TODO登録・編集画面とTODO一覧画面の切り替え用の真偽値"editing"をPropsとして取得しています。また、BrowserRouterをRouteのurlを指定できるMemoryRouterに変更しています。各Routeのelementに設定されているコンポーネントのPropsにはレンダリングに必要なデータをそれぞれ設定してます。コンポーネントファイルの準備が出来たので、各画面のスナップショットを作成し、テストを実施します。
サインイン画面テストの実行
サインイン画面のスナップショットテストを実行するSignIn.test.jsを以下に習って作成しましょう。作成出来たら、srcフォルダに保存しましょう。
SignIn.test.js
import renderer from 'react-test-renderer'
import { App } from './AppForTest';
const loadedCollection = [ //テスト用のデータベースのデータを作成
{task:"to learn React", created_date: "", finished:false, id:"abc"},
{task:"to study English", created_date: "", finished:false, id:"def"},
{task:"to meet friends", created_date: "", finished:false, id:"ghi"}
];
describe('snapshot test of TODO App.', () => {
test('snapshot test of SignIn page', () => {
const tree = renderer.create(<App url={"/signin"} loadedCollection={loadedCollection}/>).toJSON(); //サインイン画面のURLとテスト用のデータベースのデータをAppのPropsに設定
expect(tree).toMatchSnapshot();
});
});
SignIn.test.jsを実行すると、初回の実行であればターミナルに以下のメッセージが表示されると思います。
PASS src/SignIn.test.js
snapshot test of TODO App.
√ snapshot test of SignIn page (73 ms)
› 1 snapshot written.
Snapshot Summary
› 1 snapshot written from 1 test suite.
Test Suites: 1 passed, 1 total
Tests: 1 passed, 1 total
Snapshots: 1 written, 1 total
Time: 3.58 s
Ran all test suites matching /src\\SignIn.test.js/i.
スナップショットが作成されたとの内容が表示されます。__snapshots__フォルダには以下のSignIn.test.js.snapが作成されると思います。
SignIn.test.js.snap
// Jest Snapshot v1, https://goo.gl/fbAQLP
exports[`snapshot test of TODO App. snapshot test of SignIn page 1`] = `
<div
className="App"
>
<div>
<h1
style={
Object {
"color": "red",
"fontSize": "100px",
}
}
>
Simple To Do App.
</h1>
</div>
<div>
<button
onClick={[Function]}
>
<p>
サインイン
</p>
</button>
</div>
</div>
`;
アプリタイトルとサインインボタンの表示が確認できます。SignIn.test.jsを再度実行すると、以下の表示がされます。
PASS src/SignIn.test.js
snapshot test of TODO App.
√ snapshot test of SignIn page (130 ms)
Test Suites: 1 passed, 1 total
Tests: 1 passed, 1 total
Snapshots: 1 passed, 1 total
Time: 4.894 s
Ran all test suites matching /src\\SignIn.test.js/i.
テストにパスしたとの結果が表示されます。表示が変更されていないということが分かります。
TODO登録・編集画面テストの実行
同様にして、TODO登録・編集画面のテストを実行してみましょう。実行用ファイルEdit.test.jsをsrcフォルダに作成してください。内容は以下のようになります。
Edit.test.js
import renderer from 'react-test-renderer'
import { App } from './AppForTest';
const loadedCollection = [ //テスト用のデータベースのデータを作成
{task:"to learn React", created_date: "", finished:false, id:"abc"},
{task:"to study English", created_date: "", finished:false, id:"def"},
{task:"to meet friends", created_date: "", finished:false, id:"ghi"}
];
describe('snapshot test of TODO App.', () => {
test('snapshot test of Edit page', () => {
const tree = renderer.create(<App url={"/to_do/edit"} loadedCollection={loadedCollection}/>).toJSON(); //TODO登録・編集画面のURLとテスト用のデータベースのデータをAppのPropsに設定
expect(tree).toMatchSnapshot();
});
});
初回の実行後、SignIn.test.js実行時と同様のメッセージがターミナルに表示されます。__snapshots__フォルダには以下のEdit.test.js.snapが作成されると思います。
// Jest Snapshot v1, https://goo.gl/fbAQLP
exports[`snapshot test of TODO App. snapshot test of Edit page 1`] = `
<div
className="App"
>
<div>
<div
style={
Object {
"display": "flex",
"justifyContent": "space-between",
}
}
>
<button
onClick={[Function]}
style={
Object {
"height": "50px",
}
}
>
表示
</button>
<button
onClick={[Function]}
style={
Object {
"height": "50px",
}
}
>
サインアウト
</button>
</div>
<fieldset>
<legend>
GEST
さんのTo Do
</legend>
<div
style={Object {}}
>
<ul>
<li>
<div
style={
Object {
"display": "flex",
"justifyContent": "space-between",
"margin": "0 auto",
"padding": "10px",
"textAlign": "left",
}
}
>
<label>
to learn React
</label>
<button
onClick={[Function]}
style={
Object {
"display": "inlineBlock",
}
}
>
編集
</button>
</div>
</li>
<li>
<div
style={
Object {
"display": "flex",
"justifyContent": "space-between",
"margin": "0 auto",
"padding": "10px",
"textAlign": "left",
}
}
>
<label>
to study English
</label>
<button
onClick={[Function]}
style={
Object {
"display": "inlineBlock",
}
}
>
編集
</button>
</div>
</li>
<li>
<div
style={
Object {
"display": "flex",
"justifyContent": "space-between",
"margin": "0 auto",
"padding": "10px",
"textAlign": "left",
}
}
>
<label>
to meet friends
</label>
<button
onClick={[Function]}
style={
Object {
"display": "inlineBlock",
}
}
>
編集
</button>
</div>
</li>
</ul>
<button
onClick={[Function]}
style={
Object {
"backgroundColor": "red",
"color": "white",
"fontSize": "20px",
"height": "50px",
}
}
>
新規登録
</button>
</div>
</fieldset>
</div>
</div>
`;
テスト用に設定したデータベースのTODOの内容と編集ボタン、新規登録ボタンなどの表示が確認できます。Edit.test.jsを再度実行すると、SignIn.test.jsの時同様にテストにパスした結果が表示されます。
TODO一覧画面テストの実装
最後にTODO一覧画面のテストを実行してみましょう。実行用ファイルList.test.jsをsrcフォルダに作成してください。内容は以下のようになります。
Lsit.test.js
import renderer from 'react-test-renderer'
import { App } from './AppForTest';
const loadedCollection = [ //テスト用のデータベースのデータを作成
{task:"to learn React", created_date: "", finished:false, id:"abc"},
{task:"to study English", created_date: "", finished:false, id:"def"},
{task:"to meet friends", created_date: "", finished:false, id:"ghi"}
];
describe('snapshot test of TODO App.', () => {
test('snapshot test of List page', () => {
const tree = renderer.create(<App url={"/to_do"} loadedCollection={loadedCollection}/>).toJSON(); //TODO一覧画面のURLとテスト用のデータベースのデータをAppのPropsに設定
expect(tree).toMatchSnapshot();
});
});
初回の実行後、SignIn.test.js実行時と同様のメッセージがターミナルに表示され、__snapshots__フォルダには以下のList.test.js.snapが作成されると思います。
List.test.js.snap
exports[`snapshot test of TODO App. snapshot test of List page 1`] = `
<div
className="App"
>
<div>
<div
style={
Object {
"display": "flex",
"justifyContent": "space-between",
}
}
>
<button
onClick={[Function]}
style={
Object {
"height": "50px",
}
}
>
編集
</button>
<button
onClick={[Function]}
style={
Object {
"height": "50px",
}
}
>
サインアウト
</button>
</div>
<fieldset>
<legend>
GEST
さんのTo Do
</legend>
<div
style={Object {}}
>
<ul>
<li>
<div
style={
Object {
"display": "flex",
"justifyContent": "space-between",
"margin": "0 auto",
"padding": "10px",
"textAlign": "left",
}
}
>
<label
style={
Object {
"textDecoration": "none",
}
}
>
to learn React
<input
checked={false}
onChange={[Function]}
type="checkbox"
/>
</label>
<button
onClick={[Function]}
style={
Object {
"display": "inlineBlock",
}
}
>
削除
</button>
</div>
</li>
<li>
<div
style={
Object {
"display": "flex",
"justifyContent": "space-between",
"margin": "0 auto",
"padding": "10px",
"textAlign": "left",
}
}
>
<label
style={
Object {
"textDecoration": "none",
}
}
>
to study English
<input
checked={false}
onChange={[Function]}
type="checkbox"
/>
</label>
<button
onClick={[Function]}
style={
Object {
"display": "inlineBlock",
}
}
>
削除
</button>
</div>
</li>
<li>
<div
style={
Object {
"display": "flex",
"justifyContent": "space-between",
"margin": "0 auto",
"padding": "10px",
"textAlign": "left",
}
}
>
<label
style={
Object {
"textDecoration": "none",
}
}
>
to meet friends
<input
checked={false}
onChange={[Function]}
type="checkbox"
/>
</label>
<button
onClick={[Function]}
style={
Object {
"display": "inlineBlock",
}
}
>
削除
</button>
</div>
</li>
</ul>
</div>
</fieldset>
</div>
</div>
`;
テスト用に設定したデータベースのTODOの内容、チェックボックス、削除ボタンなどの表示が確認できます。List.test.jsを再度実行すると、他と同様にテストにパスした結果が表示されます。
