ローカル環境で Cypress を使って E2E テストを書く
はじめに
今回のコードはこちら↓
開発環境
"react": "^18.3.1",
"vite": "^5.3.4"
"cypress": "^13.13.1",
"start-server-and-test": "^2.0.4",
Cypress とは?
Cypress comes fully baked, batteries included. Here is a list of things it can do that no other testing framework can:
- Time Travel: Cypress takes snapshots as your tests run. Hover over commands in the Command Log to see exactly what happened at each step.
- Debuggability: Stop guessing why your tests are failing. Debug directly from familiar tools like Developer Tools. Our readable errors and stack traces make debugging lightning fast.
- Automatic Waiting: Never add waits or sleeps to your tests. Cypress automatically waits for commands and assertions before moving on. No more async hell.
- Spies, Stubs, and Clocks: Verify and control the behavior of functions, server responses, or timers. The same functionality you love from unit testing is right at your fingertips.
- Network Traffic Control: Easily control, stub, and test edge cases without involving your server. You can stub network traffic however you like.
- Consistent Results: Our architecture doesn't use Selenium or WebDriver. Say hello to fast, consistent and reliable tests that are flake-free.
- Screenshots, Videos, and Test Replay: View screenshots taken automatically on failure, or videos, if enabled, of your entire test suite when run from the CLI. Record to Cypress Cloud and replay the test as it executed during the run for zero-configuration debugging using Test Replay.
- Cross Browser Testing: Run tests within Firefox and Chrome-family browsers (including Edge and Electron) locally and optimally in a Continuous Integration pipeline.
- Smart Orchestration: Once you're set up to record to Cypress Cloud, easily parallelize your test suite, rerun failed specs first with Spec Prioritization, and cancel test runs on failures with Auto Cancellation for tight feedback loops.
- Flake Detection: Discover and diagnose unreliable tests with Cypress Cloud's Flaky test management.
Cypress を導入する
今回は Vite + React + Typescript でプロジェクトを作成する。
環境構築に関しては下記を参照。
Cypress をインストールする。
npm install cypress --save-dev
npm scripts に下記を追加する。
"scripts": {
...
"cy:open": "cypress open"
},
Cypress を起動する。
npm run cy:open
今回は、Vite + React で作成しているので、上記コマンドを実行後に作成されたcypress.config.ts
のコードに下記コードを追加する。
import { defineConfig } from "cypress";
export default defineConfig({
e2e: {
setupNodeEvents(on, config) {
// implement node event listeners here
},
+ baseUrl: "http://localhost:8080",
},
+ component: {
+ devServer: {
+ framework: "react",
+ bundler: "vite",
+ },
+ }
});
上記手順で構築していると、おそらく下記エラーが出る。
our configFile is invalid: [プロジェクトパス]/cypress.config.ts
It threw an error when required, check the stack trace below:
ReferenceError: exports is not defined in ES module scope
このエラーはpackage.json
に"type": "module"
が定義されており、ES module
として扱われてしまうため。"type": "module"
を削除する、もしくはcypress.config.ts
をcypress.config.cts
に変更しないといけない。今回は、前者で対応する。
エラーが解決すると下記画面が表示される。
今回は E2E を行いたいので、E2E Testing
をクリックする。
Chrome
を選択する。
Create new spec
をクリックして、テストファイルを作成する。
作成したファイルを実行して成功すれば、 Cypress の導入は成功!
Cypress でテストコードを書く
上記で作成したspec.cy.ts
のコードを下記のように変更する。
describe('The Home Page', () => {
it('passes', () => {
- cy.visit('https://example.cypress.io')
+ cy.visit('http://localhost:8080')
})
})
すると下記添付画像のようなエラーが、、、
ローカルサーバーが見当たらないことによるエラーです。
解決方法はいくつかありますが、今回は公式に記載がある方法で行う。
まずは必要なパッケージのインストールする。
npm install --save-dev start-server-and-test
下記 npm script を追加する。
start-server-and-test [サーバーを起動する script] [開発環境URL] cy:open
のように指定すると良い。
"scripts": {
...
"dev": "vite",
"cy:open": "cypress open",
+ "cy:start-server-and-test": "start-server-and-test dev http://localhost:8080 cy:open"
},
現時点での Vite + React のデフォルトの UI は下記添付画像のようになっている。
App.tsx
import { useState } from 'react'
import reactLogo from './assets/react.svg'
import viteLogo from '/vite.svg'
import './App.css'
function App() {
const [count, setCount] = useState(0)
return (
<>
<div>
<a href="https://vitejs.dev" target="_blank">
<img src={viteLogo} className="logo" alt="Vite logo" />
</a>
<a href="https://react.dev" target="_blank">
<img src={reactLogo} className="logo react" alt="React logo" />
</a>
</div>
<h1>Vite + React</h1>
<div className="card">
<button onClick={() => setCount((count) => count + 1)}>
count is {count}
</button>
<p>
Edit <code>src/App.tsx</code> and save to test HMR
</p>
</div>
<p className="read-the-docs">
Click on the Vite and React logos to learn more
</p>
</>
)
}
export default App
試しに、ボタンをクリックした際にカウントがアップしているかのテストコードを実装する。
dom 要素の指定でも問題なく動くが、テストコードが増えることも考慮して、対象 dom 要素にテスト用の属性を付与する。
- <button onClick={() => setCount((count) => count + 1)}>
+ <button data-e2e="e2e-button" onClick={() => setCount((count) => count + 1)}>
Cypress を導入するで作成したhome.cy.ts
のコードを下記コードに差し替える。
describe('test', () => {
it('count test', () => {
cy.visit('/')
cy.get(`[data-e2e="e2e-button"]`).click()
expect(cy.get(`[data-e2e="e2e-button"]`).contains("count is 1"))
})
})
npm run cy:start-server-and-test
を実行し、テストが通れば成功。
念の為、失敗パターンも試してみる。
- cy.get(`[data-e2e="e2e-button"]`).click()
+ cy.get(`[data-e2e="e2e-butto"]`).click()
ちゃんと失敗していることが確認できたので、正常にコードが動いていることが確認できる。
以上!
Discussion