この記事は「Reqnroll × Playwrightで始めるE2Eテスト入門」シリーズの一部です。
.NETのReqnrollプロジェクトでPlaywrightを使うときのコード構成(基本)
E2Eテストを.NETで実装する場合、BDDツールである Reqnroll と、ブラウザ自動化ツール Playwright を組み合わせるケースが増えています。
しかし、実際に実装しようとすると次の疑問が出てきます。
- Playwrightの初期化コードはどこに書くのか
- StepDefinitionからどうやってブラウザを操作するのか
- Pageオブジェクトはどこで管理するのか
この記事では、ReqnrollプロジェクトでPlaywrightを使うときの基本的なコード構成を解説します。
※この記事では環境構築の説明は省略し、コード構成にフォーカスします。環境構築は以下の記事で解説。
⇒ Reqnroll + Playwrightの環境構築手順(.NET / C#)
Reqnroll + Playwrightの基本構成
ReqnrollでPlaywrightを使う場合、次のような構成にするのが一般的です。
TestProject
├ Hooks
│ └ PlaywrightHooks.cs
│
├ Pages
│ └ LoginPage.cs
│
├ Steps
│ └ LoginSteps.cs
│
└ Features
└ Login.feature
それぞれの役割は次の通りです。
| フォルダ | 役割 |
|---|---|
| Features | BDDのシナリオを書く |
| Steps | シナリオの実装 |
| Hooks | Playwrightの初期化 |
| Pages | PageObject |
このように役割ごとにフォルダを分けることで、テストコードが増えても管理しやすくなります。
Playwrightの初期化(Hooks)
Playwrightのブラウザ起動処理は、ReqnrollのHooksに書くのが一般的です。
⇒ Hooksの解説は次の記事で確認できます。
ReqnrollのHooksとは?シナリオ前後で処理を実行する方法を解説(.NET)
Reqnrollには BeforeScenario と AfterScenario があり、テストの前後処理をまとめて管理できます。
例として、Playwrightの初期化コードは次のようになります。
using Microsoft.Playwright;
using Reqnroll;
[Binding]
public class PlaywrightHooks
{
public static IPage Page;
private static IBrowser browser;
private static IPlaywright playwright;
[BeforeScenario]
public async Task BeforeScenario()
{
playwright = await Playwright.CreateAsync();
browser = await playwright.Chromium.LaunchAsync(new BrowserTypeLaunchOptions
{
Headless = false
});
var context = await browser.NewContextAsync();
Page = await context.NewPageAsync();
}
[AfterScenario]
public async Task AfterScenario()
{
await browser.CloseAsync();
playwright.Dispose();
}
}
このクラスでは次の処理を行っています。
- Playwrightの起動
- ブラウザの起動
- Pageオブジェクトの作成
このPageをStepDefinitionから利用します。
PlaywrightHooksクラスの解説
PlaywrightHooksクラスの役割
このクラスは、Reqnroll のHooks機能を使い、
テスト実行の前後で Playwright のブラウザを起動・終了するためのクラスです。
Reqnrollでは Hooks を使うことで、
- テスト開始前の処理
- テスト終了後の処理
を共通化できます。
このコードでは、Scenarioごとにブラウザを起動し、終了時に閉じる構成になっています。
usingディレクティブ
using Microsoft.Playwright;
using Reqnroll;
ここでは必要なライブラリを読み込んでいます。
| 名前空間 | 役割 |
|---|---|
| Microsoft.Playwright | PlaywrightのAPI |
| Reqnroll | ReqnrollのBDD機能 |
Playwrightを操作するためのクラス(IPageやIBrowserなど)は、Microsoft.Playwright 名前空間に含まれています。
Binding属性
[Binding]
public class PlaywrightHooks
[Binding] は Reqnrollがこのクラスをテスト実行時に認識するための属性です。
この属性が付いているクラスは、
- StepDefinition
- Hooks
としてReqnrollに登録されます。
Hooksとして機能させる場合も、この属性が必要になります。
フィールド(変数)の定義
public static IPage Page;
private static IBrowser browser;
private static IPlaywright playwright;
ここではPlaywrightの主要なオブジェクトを定義しています。
| 変数 | 役割 |
|---|---|
| IPlaywright | Playwright本体 |
| IBrowser | ブラウザインスタンス |
| IPage | ブラウザのタブ |
Playwrightでは次の階層でブラウザを操作します。
Playwright
↓
Browser
↓
BrowserContext
↓
Page
今回のコードでは、最終的に PageをStepDefinitionから使えるようにしています。
Pageをpublic staticにしているのは、
StepDefinitionクラスから参照できるようにするためです。
例
await PlaywrightHooks.Page.GotoAsync("https://example.com");
BeforeScenario
[BeforeScenario]
public async Task BeforeScenario()
[BeforeScenario] は、
シナリオ実行前に呼び出されるメソッドです。
つまり
Scenario開始
↓
BeforeScenario実行
↓
StepDefinition実行
という順番になります。
Playwrightの起動
playwright = await Playwright.CreateAsync();
ここではPlaywrightのインスタンスを作成しています。
Playwrightを使うときは、まずこのオブジェクトを作る必要があります。
ブラウザの起動
browser = await playwright.Chromium.LaunchAsync(
new BrowserTypeLaunchOptions
{
Headless = false
});
ここではChromiumブラウザを起動しています。
Headless の設定によって、ブラウザ表示の有無が変わります。
| 設定 | 意味 |
|---|---|
| true | 画面を表示しない |
| false | ブラウザを表示する |
テストのデバッグ時は、falseにしておくとブラウザ動作を確認できます。
BrowserContextの作成
var context = await browser.NewContextAsync();
BrowserContext は、ブラウザの独立したセッションです。
イメージとしては
ブラウザ
├ シークレットセッション1
├ シークレットセッション2
のような構造になります。
テストでは、シナリオごとに新しいContextを作ることで
- Cookie
- LocalStorage
- セッション情報
を分離できます。
Pageの作成
Page = await context.NewPageAsync();
ここで ブラウザタブ(Page) を作成しています。
Playwrightで実際に操作するのは、このPageオブジェクトです。
例えば
Page.GotoAsync()
Page.ClickAsync()
Page.FillAsync()
などの操作を行います。
AfterScenario
[AfterScenario]
public async Task AfterScenario()
[AfterScenario] は
シナリオ終了後に呼ばれるメソッドです。
処理の流れ
Scenario終了
↓
AfterScenario実行
ブラウザ終了
await browser.CloseAsync();
ここではブラウザを閉じています。
テストごとにブラウザを終了することで、
- メモリリーク防止
- テストの独立性確保
ができます。
Playwrightの破棄
playwright.Dispose();
Playwrightのリソースを解放しています。
.NETでは、外部リソースを使うライブラリは
Disposeを呼んで解放するのが推奨されています。
このコードの処理の流れ
最終的にこのHooksは次のように動きます。
Scenario開始
↓
BeforeScenario
↓
Playwright起動
↓
Browser起動
↓
Context作成
↓
Page作成
↓
StepDefinition実行
↓
AfterScenario
↓
Browser終了
↓
Playwright破棄
StepDefinitionからPlaywrightを使う
StepDefinitionでは、Hooksで作成したPageを利用してブラウザ操作を行います。
例として、ログインページを操作するStepDefinitionは次のように書けます。
using Reqnroll;
[Binding]
public class LoginSteps
{
[Given(@"ログインページを開く")]
public async Task OpenLoginPage()
{
await PlaywrightHooks.Page.GotoAsync("https://example.com/login");
}
[When(@"ユーザー名とパスワードを入力する")]
public async Task InputLogin()
{
await PlaywrightHooks.Page.FillAsync("#user", "test");
await PlaywrightHooks.Page.FillAsync("#password", "password");
}
[When(@"ログインボタンを押す")]
public async Task ClickLogin()
{
await PlaywrightHooks.Page.ClickAsync("#login");
}
}
このようにすると、次の流れでテストが実行されます。
Feature → StepDefinition → Playwright → ブラウザ操作
PageObjectを使った構成
テストコードが増えてくると、StepDefinitionに直接Playwrightの操作を書くと管理が難しくなります。
そのため、多くのプロジェクトでは PageObjectパターン を使います。
例として、ログインページのPageObjectは次のように書きます。
using Microsoft.Playwright;
public class LoginPage
{
private readonly IPage page;
public LoginPage(IPage page)
{
this.page = page;
}
public async Task Open()
{
await page.GotoAsync("https://example.com/login");
}
public async Task Login(string user, string password)
{
await page.FillAsync("#user", user);
await page.FillAsync("#password", password);
await page.ClickAsync("#login");
}
}
StepDefinitionでは、このPageObjectを利用します。
[Binding]
public class LoginSteps
{
private readonly LoginPage loginPage;
public LoginSteps()
{
loginPage = new LoginPage(PlaywrightHooks.Page);
}
[Given(@"ログインページを開く")]
public async Task OpenPage()
{
await loginPage.Open();
}
}
この構成にすると、次のメリットがあります。
- StepDefinitionがシンプルになる
- UI変更の影響を受けにくい
- 同じ操作を複数テストで再利用できる
Reqnroll + Playwrightの処理の流れ
最終的にテストは次のような構造になります。
Featureファイル
↓
StepDefinition
↓
PageObject
↓
Playwright
↓
ブラウザ操作
このような構成にすることで、E2Eテストが増えても保守しやすいコードになります。
まとめ
.NETのReqnrollプロジェクトでPlaywrightを使う場合は、次の構成にすると管理しやすくなります。
基本構成
- Hooks
Playwrightの初期化 - StepDefinitions
シナリオの実装 - Pages
PageObject
処理の流れ
Feature
→ StepDefinition
→ PageObject
→ Playwright
このような構成にしておくことで、テストコードが増えても整理されたE2Eテストを維持することができます。
