テストシナリオの構成と読み解き方
まずは実物を見てみよう:テストコードの全体像
一見すると複雑に見えるかもしれませんが、ご安心ください。次のセクションから、このコードがどのようなルールで組み立てられているのかを、一つずつ丁寧に紐解いていきます。
本格的な実装(【実行:Act】)に挑戦するのは後のレッスンからです。その前にテストの骨格となる【準備:Arrange】と【検証:Assert】の考え方について学んでいきましょう。
コードの冒頭にある import とは?
※今回の課題では使用しませんが、知識として覚えておくと役立ちます。
プログラミングにおけるimportは、大工さんが作業を始める前に、道具箱から「カナヅチ」や「ノコギリ」といった必要な道具を取り出して準備する作業に似ています。
Playwrightでテストを書く際も、最初に「こんな道具を使ってテストを書きますよ!」という宣言が必要なのです。
import { expect } from "@playwright/test";
Playwrightという大きな「公式ツールボックス」から、『expect(結果をチェックするための虫眼鏡)』という道具を取り出しています。
import { test } from "@setup/test";
こちらは、このプロジェクト専用に用意された「特注ツールボックス(@setup/test)」から、『test(テストの土台となる設計図)』という道具を取り出しています。
今は、「importは、テストで使うお決まりの道具を準備しているんだな」という程度の理解で全く問題ありません。
シナリオを構成する2つの主役:describe() と test()
Playwrightのテストシナリオは、物語を組み立てるように、主に2つの要素で構成されています。
describe():物語の「タイトル」
| 項目 | 内容 |
|---|---|
| 役割 | 関連するテストを一つのグループにまとめる「大見出し」です |
| 例え | 「第一章:ログイン機能に関するテスト」のように、テストの大きなテーマを宣言します |
| メリット | 「ログイン」や「商品購入」のように機能単位でテストをまとめることで、テスト全体の見通しが非常に良くなります |
test():物語の「各エピソード」
| 項目 | 内容 |
|---|---|
| 役割 | テストしたい一つの具体的なシナリオ(=テストケース)です。これが実際に実行されるテストの最小単位になります |
| 例え | 「正しい情報でログインできる」「パスワードを間違えるとエラーが表示される」といった、物語の中の個別のエピソードにあたります |
テストの基本3ステップ「準備 → 実行 → 検証」
テストシナリオは、基本的に以下の3つのステップで構成されています。これはテストにおける非常に重要な考え方で、「Arrange-Act-Assert (AAA)」 とも呼ばれます。
| ステップ | 英語 | 説明 |
|---|---|---|
| 準備 | Arrange | テストを実行するための前準備をする段階。例:ログインページを開く、サインアップする、テスト用のデータを準備する など |
| 実行 | Act | テストしたい操作を実際に行う段階。例:メールアドレスとパスワードを入力して、ログインボタンをクリックする など |
| 検証 | Assert | 実行した結果が、期待通りになったかを確認する段階。例:「ようこそ、山田さん」というメッセージが表示されていることを確認する など |
この「準備 → 実行 → 検証」の流れを意識するだけで、テストコードの目的が格段に分かりやすくなります。
具体的なテストの例
言葉だけだと少し分かりにくいかもしれませんので、具体的なテストの例を見て、この3ステップのイメージを掴みましょう。
テストの例①: 登録したデータがテーブルに表示されていることを確認する
| ステップ | 内容 |
|---|---|
| 準備 | ユーザー登録を済ませておく |
| 実行 | 新しいデータを登録する |
| 検証 | 登録したデータがテーブルに表示されていることを確認する |
テストの例②: 登録済みデータを編集できることを確認する
| ステップ | 内容 |
|---|---|
| 準備 | ユーザー登録と、編集対象のデータ登録を済ませておく |
| 実行 | 登録済みのデータを編集する |
| 検証 | データが正しく編集されていることを確認する |
例②のように、テストしたい内容(実行)によっては、準備することが多くなるのが分かりますね。
実際のソースコードでは画像のようになります。これは1つのテストファイルに、2つのテストシナリオがある場合のAAAです。後ほど詳しく説明するので今はさっと見るだけで問題ありません。
共通の「準備」をまとめる機能:beforeEach() / beforeAll()
マイページの機能をテストする際に毎回「ログイン」が必要になるケースのように、複数のテストケースで同じ「準備」が必要な場合、beforeEach() や beforeAll() を使うと、コードを非常にすっきりとまとめることができます。
| 関数 | 実行タイミング |
|---|---|
beforeEach() |
ファイル内の各テストが実行される直前に 毎回 実行されます |
beforeAll() |
ファイル内の最初のテストが始まる前に 一度だけ 実行されます |
beforeEach() の使用例
テストシナリオに入る前に、必ず beforeEach() の中の処理が実行されています。
今回の場合、テストシナリオが3つあるので、3回 beforeEach() に入っていることがターミナルの結果からわかります。
beforeAll() の使用例
テスト実行時、1度だけ beforeAll() の中の処理が実行されています。
こちらはテストシナリオが3つありますが、1回のみ beforeAll() に入っていることがターミナルの結果からわかります。
準備フェーズの考え方
「準備」がないテストもある?
あります。 例えば「ログインページを開き、ログインできることを確認する」という単純なテストでは、「ページを開く」準備と「ログインを試す」実行がほぼ一体化しています。
このように、テストの目的によっては、明確な「準備」フェーズを設けないこともあります。
beforeEach() や beforeAll() は、いつ使うべき?
ここで悩むかもしれませんが、原則はとてもシンプルです。
「ファイル内の複数のテストシナリオで、全く同じ準備が必要な場合(要はデータを使い回す)」に使うのが基本です。
beforeEach() や beforeAll() の一番の目的は、同じ準備コードの繰り返しをなくし、コードを綺麗に保つことだからです。
ファイル内にテストシナリオが1つしかない場合は?
理屈上は、テストシナリオが1つなら繰り返しがないので beforeEach()/beforeAll() は不要です。
しかし、現場によっては、準備と実行の分離を明確にするため、あえて beforeEach()/beforeAll() で準備を囲むルールを採用していることもあります。
なのでチームのルールに従いましょう。 迷ったときは、プロジェクト内の他のテストコードを読んで、書き方を真似するのが最も確実な方法です。
このレッスンで覚えておいてほしいこと
| 関数 | 役割 |
|---|---|
describe() |
関連テストをまとめる「大見出し(タイトル)」を書くところ |
test() |
1つの具体的な「テストケース(エピソード)」を書くところ |
beforeEach() |
各 test() の前に必ず実行される処理 |
beforeAll() |
グループの最初の test() の前に一度だけ実行される処理 |
この4つの役割を覚えてもらえたらOKです!