直前の実行時に失敗したテストケースのみ再実行 in Playwright
はじめに
実現したいこと
Playwright では公式ドキュメントの Retries のページで紹介されているように実行中に失敗したケースを再実行する機能が提供されている。この機能はテスト実行時に何かしらの要因によって失敗したテストケース、すなわち不安定なテストケースを同じテスト実行のプロセス中に再実行する機能である。しかし、テスト実行が完了した結果、失敗したテストケースのみを再度実行する機能は Playwright では提供されていない。この記事では、直前に実行したテストの結果から失敗したテストケースのみを再実行する方法を紹介する。
使い所
例えば、テスト実行時にデータベースに特定のテストデータを用意しておく必要がある特定のテストケースがあるとする。テスト実行時に、この必要なテストデータが用意できていない、もしくは意図したものになっていない場合、そのテストケースは失敗する。他のテストケースには、そのテストデータは依存しないため、テスト実行時に問題なく成功する。テスト実行後にテストデータの欠陥に気づき、テストデータを修正した後に、そのテストケースのみを再実行したいという場合に、前述の「以前に実行したテストの結果から失敗したテストケースのみを再実行する方法」があると便利である。
実装
Playwright で特定のテストケースを実行する方法
公式ドキュメントの Command line で紹介されているように、以下の指定方法がある。
-
filePath:lineNumber
の指定npx playwright test my-spec.ts:42
-
testTitle
の指定npx playwright test -g "add a todo item"
今回はひとつ目の filePath:lineNumber
を指定する方法を使用する。
レポート機能による実行結果の保存
前述の filePath:lineNumber
の情報をテスト実行時に保存する必要がある。Playwright では、テスト実行結果をレポートとして保存する機能が提供されている。この機能を使用して、テスト実行結果を保存する。レポートの出力には、独自実装したレポート機能を使用することができる。テストケースごとに filePath:lineNumber
の情報と outcome="expected"|"unexpected"|"skipped"|"flaky"
の情報を含む配列を JSON 形式で出力するレポート機能を実装した。以下のリポジトリで公開している。
レポートの出力例を以下に示す。
{
"startedAt": 1713507478073,
"durationInMs": 27545.525999999998,
"status": "passed",
"results": [
{
"id": "308f7d0e05acf652cf55-dff0c71519df34ca7ba8",
"project": "setup",
"location": "setup/login.setup.ts:22:5",
"title": "Login and Setup as Bob",
"outcome": "expected",
"durationInMs": 5113
},
{
"id": "884fc53766d15c58cb3b-f843d31e795e7fc28ff0",
"project": "chrome",
"location": "tests/chat.spec.ts:129:7",
"title": "Send a chat message",
"outcome": "expected",
"durationInMs": 6602
},
{
"id": "269790c8a8e01a6a79d9-a66c0172f374246b12a5",
"project": "teardown",
"location": "teardown/chat.teardown.ts:77:7",
"title": "Delete chat messages",
"outcome": "expected",
"durationInMs": 10126
}
]
}
実行ごとにレポートの出力先を変更する
Playwright の GitHub リポジトリの Issue#19010 を参考にして、実行した時刻をフォルダ名に含めて、実行ごとにレポートの出力先を変更するようにした。
また、一意に最新の結果のフォルダを参照するために、シンボリックリンクを作成するようにした。ここでは、そのスクリプトは割愛する。
import moment from "moment";
import { defineConfig } from "@playwright/test";
const reportFolder = `report/report-${moment().format("YYYY-MM-DD[T]HH-mm-ss")`;
export default defineConfig({
reporter: [
[
"playwright-simple-json-reporter",
{
outputFolder: reportFolder,
name: "result.json",
testMatch: /.*\.spec\.ts/,
},
],
],
});
前回失敗したテストケースのみを再実行する
outcome
が unexpected
のテストケースのみを実行する。
const report = JSON.parse(fs.readFileSync(latestReport).toString());
if (report.status !== "passed"){
const targets = report.results.filter(
(result) => result.outcome === "unexpected"
).map((result) => result.location);
proc.execSync(`npm test -- ${targets.join(" ")} ${process.argv.slice(2).join(" ")}`, {stdio: 'inherit'});
} else {
console.info("There is no failed test case in the latest report");
}
Discussion