この記事は公開されてから1年以上経過しています。情報が古い可能性がありますので、ご注意ください。
はじめに
テントの中から失礼します、CX事業本部のてんとタカハシです!
E2E テストを実装する際、API のレスポンスを待ってから、何かの要素をチェックしたいケースがあります。Cypress では、Fetch による API の呼び出しがデフォルトで監視できず、別途オプションを付け足す必要があったのですが、Version 6 からはそれが不要になったようです。
テスト対象
例えば、従業員のデータを API から取得して、リストを作成するページがあったとします。API の呼び出しには Fetch を使用しています。
App.tsx
import React, { FC, useEffect, useState } from 'react';
import './App.css';
type Employee = {
id: number;
name: string;
age: number;
};
const App: FC = () => {
const [employees, setEmployees] = useState<Employee[]>([]);
const listStyle = {
marginBottom: '4px',
};
useEffect(() => {
fetch('http://localhost:3001/employees')
.then((response) => response.json())
.then((data) => setEmployees(data));
}, []);
return (
<div className="App">
<h3>Employees</h3>
<ul id="employees">
{employees.map((employee) => (
<li key={employee.id} style={listStyle}>
{employee.name}
</li>
))}
</ul>
</div>
);
};
export default App;
UI はこんな感じです。
API を呼び出した後、リストが作成されているか確認する E2E テストを実装するとします。
今まで(v4.9 ~ v5系)
失敗例
下記の実装で、/employees
のレスポンスを待ってから、リストが作成されているか確認できるのですが、
sample_spec.js
describe('サンプルテスト', () => {
beforeEach(() => {
cy.server();
cy.route('GET', '/employees').as('employees');
});
it('Employees API のレスポンスを待つ', () => {
cy.visit('/');
cy.wait('@employees');
cy.get('#employees > li').should('have.length', 5);
});
});
このまま E2E テストを実行しても失敗に終わります。
$ cypress run
Missing baseUrl in compilerOptions. tsconfig-paths will be skipped
====================================================================================================
(Run Starting)
┌────────────────────────────────────────────────────────────────────────────────────────────────┐
│ Cypress: 5.6.0 │
│ Browser: Electron 85 (headless) │
│ Specs: 1 found (sample_spec.js) │
└────────────────────────────────────────────────────────────────────────────────────────────────┘
────────────────────────────────────────────────────────────────────────────────────────────────────
Running: sample_spec.js (1 of 1)
サンプルテスト
1) Employees API のレスポンスを待つ
0 passing (6s)
1 failing
1) サンプルテスト
Employees API のレスポンスを待つ:
CypressError: Timed out retrying: `cy.wait()` timed out waiting `5000ms` for the 1st request to the route: `employees`. No request ever occurred.
https://on.cypress.io/wait
at cypressErr (http://localhost:3000/__cypress/runner/cypress_runner.js:172713:18)
at Object.errByPath (http://localhost:3000/__cypress/runner/cypress_runner.js:172764:10)
at checkForXhr (http://localhost:3000/__cypress/runner/cypress_runner.js:159916:33)
at http://localhost:3000/__cypress/runner/cypress_runner.js:159941:28
at tryCatcher (http://localhost:3000/__cypress/runner/cypress_runner.js:10325:23)
at Function.Promise.attempt.Promise.try (http://localhost:3000/__cypress/runner/cypress_runner.js:7599:29)
at tryFn (http://localhost:3000/__cypress/runner/cypress_runner.js:165556:21)
at whenStable (http://localhost:3000/__cypress/runner/cypress_runner.js:165594:12)
at http://localhost:3000/__cypress/runner/cypress_runner.js:165089:16
at tryCatcher (http://localhost:3000/__cypress/runner/cypress_runner.js:10325:23)
at Promise._settlePromiseFromHandler (http://localhost:3000/__cypress/runner/cypress_runner.js:8260:31)
at Promise._settlePromise (http://localhost:3000/__cypress/runner/cypress_runner.js:8317:18)
at Promise._settlePromise0 (http://localhost:3000/__cypress/runner/cypress_runner.js:8362:10)
at Promise._settlePromises (http://localhost:3000/__cypress/runner/cypress_runner.js:8442:18)
at Promise._fulfill (http://localhost:3000/__cypress/runner/cypress_runner.js:8386:18)
at http://localhost:3000/__cypress/runner/cypress_runner.js:10000:46
From Your Spec Code:
at Context.eval (http://localhost:3000/__cypress/tests?p=cypress/integration/sample_spec.js:106:8)
(Results)
┌────────────────────────────────────────────────────────────────────────────────────────────────┐
│ Tests: 1 │
│ Passing: 0 │
│ Failing: 1 │
│ Pending: 0 │
│ Skipped: 0 │
│ Screenshots: 1 │
│ Video: true │
│ Duration: 5 seconds │
│ Spec Ran: sample_spec.js │
└────────────────────────────────────────────────────────────────────────────────────────────────┘
(Screenshots)
- /Users/hogehoge/fugafuga/piyopiyo/cypress/screenshots (1280x720)
/sample_spec.js/サンプルテスト -- Employees API のレスポンスを待つ (fai…
(Video)
- Started processing: Compressing to 32 CRF
- Finished processing: /Users/hogehoge/fugafuga/piyopiyo/cypress (0 seconds)
/videos/sample_spec.js.mp4
====================================================================================================
(Run Finished)
Spec Tests Passing Failing Pending Skipped
┌────────────────────────────────────────────────────────────────────────────────────────────────┐
│ ✖ sample_spec.js 00:05 1 - 1 - - │
└────────────────────────────────────────────────────────────────────────────────────────────────┘
✖ 1 of 1 failed (100%) 00:05 1 - 1 - -
cy.wait()
した際に、API の呼び出しを監視できていなく、そのままタイムアウトしてしまうようです。
CypressError: Timed out retrying: `cy.wait()` timed out waiting `5000ms` for the 1st request to the route: `employees`. No request ever occurred.
成功例
Cypress では、v4.9 より Fetch による API の呼び出しを監視するためのオプションが追加されました。
For a long, long, loooong time, the Cypress network control could not "see" window.fetch calls and only understood XMLHttpRequest Ajax calls.
...
Meanwhile, we have added a quick fetch polyfill as an experimental feature in Cypress v4.9.0. By turning this feature on, the Cypress Test Runner will automatically replace window.fetch with a unfetch polyfill built on top of XMLHttpRequest object, making these Ajax requests "visible" to the Test Runner.
Cypress - experimental-fetch-polyfill
cypress.json
に下記を追加するだけで OK です。
cypress.json
{
...
"experimentalFetchPolyfill": true
}
これで E2E テストが成功するようになります。
$ cypress run
Missing baseUrl in compilerOptions. tsconfig-paths will be skipped
====================================================================================================
(Run Starting)
┌────────────────────────────────────────────────────────────────────────────────────────────────┐
│ Cypress: 5.6.0 │
│ Browser: Electron 85 (headless) │
│ Specs: 1 found (sample_spec.js) │
│ Experiments: experimentalFetchPolyfill=true │
└────────────────────────────────────────────────────────────────────────────────────────────────┘
────────────────────────────────────────────────────────────────────────────────────────────────────
Running: sample_spec.js (1 of 1)
サンプルテスト
✓ Employees API のレスポンスを待つ (381ms)
1 passing (409ms)
(Results)
┌────────────────────────────────────────────────────────────────────────────────────────────────┐
│ Tests: 1 │
│ Passing: 1 │
│ Failing: 0 │
│ Pending: 0 │
│ Skipped: 0 │
│ Screenshots: 0 │
│ Video: true │
│ Duration: 0 seconds │
│ Spec Ran: sample_spec.js │
└────────────────────────────────────────────────────────────────────────────────────────────────┘
(Video)
- Started processing: Compressing to 32 CRF
- Finished processing: /Users/hogehoge/fugafuga/piyopiyo (0 seconds)
====================================================================================================
(Run Finished)
Spec Tests Passing Failing Pending Skipped
┌────────────────────────────────────────────────────────────────────────────────────────────────┐
│ ✔ sample_spec.js 404ms 1 1 - - - │
└────────────────────────────────────────────────────────────────────────────────────────────────┘
✔ All specs passed! 404ms 1 1 - - -
v6 から
Version 6 からは intercept()
を使用することで、Fetch による API 呼び出しを監視できるようになりました。cypress.json
にオプションを付け足す必要はもうありません。
・ experimentalFetchPolyfill has been deprecated. We encourage you to use cy.intercept() to intercept requests using the Fetch API instead.
・ can intercept all types of network requests including Fetch API, page loads, XMLHttpRequests, resource loads, etc.
実装は下記になります。
sample_spec.js
describe('サンプルテスト', () => {
beforeEach(() => {
cy.intercept({
url: '/employees',
method: 'GET',
}).as('employees');
});
it('Employees API のレスポンスを待つ', () => {
cy.visit('/');
cy.wait('@employees');
cy.get('#employees > li').should('have.length', 5);
});
});
ちゃんと E2E テストが成功しました。
$ cypress run
Missing baseUrl in compilerOptions. tsconfig-paths will be skipped
====================================================================================================
(Run Starting)
┌────────────────────────────────────────────────────────────────────────────────────────────────┐
│ Cypress: 6.0.1 │
│ Browser: Electron 87 (headless) │
│ Specs: 1 found (sample_spec.js) │
└────────────────────────────────────────────────────────────────────────────────────────────────┘
────────────────────────────────────────────────────────────────────────────────────────────────────
Running: sample_spec.js (1 of 1)
サンプルテスト
✓ Employees API のレスポンスを待つ (369ms)
1 passing (404ms)
(Results)
┌────────────────────────────────────────────────────────────────────────────────────────────────┐
│ Tests: 1 │
│ Passing: 1 │
│ Failing: 0 │
│ Pending: 0 │
│ Skipped: 0 │
│ Screenshots: 0 │
│ Video: true │
│ Duration: 0 seconds │
│ Spec Ran: sample_spec.js │
└────────────────────────────────────────────────────────────────────────────────────────────────┘
(Video)
- Started processing: Compressing to 32 CRF
- Finished processing: /Users/hogehoge/fugafuga/piyopiyo (0 seconds)
====================================================================================================
(Run Finished)
Spec Tests Passing Failing Pending Skipped
┌────────────────────────────────────────────────────────────────────────────────────────────────┐
│ ✔ sample_spec.js 385ms 1 1 - - - │
└────────────────────────────────────────────────────────────────────────────────────────────────┘
✔ All specs passed! 385ms 1 1 - - -
おわりに
Cypress のちょいネタでした。Fetch による API 呼び出しを行う際の、ちょっとした躓きポイントが解消されましたね。
今回は以上になります。最後まで読んで頂きありがとうございました!