測試使用者流程
在生產環境中除錯是場惡夢。您必須檢查應用程式的每一層。是元件錯誤、事件觸發錯誤、樣式問題、應用程式狀態,還是 API 損壞?以上皆有可能,而您必須釐清原因。
UI 協助人們在多個頁面上瀏覽一連串步驟,以達成其目標。到目前為止,我們已了解 Storybook 如何輕鬆隔離每個頁面,並在這些頁面上執行視覺、可及性和互動測試。但是,為了驗證整個流程並找出整合問題,您需要端對端 (E2E) UI 測試。
您的 UI 是否端對端皆可運作?
使用者流程不只包含一個元件,它們牽涉到多個元件協同運作。每次互動都會觸發狀態更新、路由變更和 API 呼叫,這些都會影響螢幕上呈現的內容。由於有這麼多故障點,逐一進行 QA 可能會很困難。
團隊使用 E2E 測試來確保使用者體驗如預期般運作。若要執行 E2E 測試,您首先要啟動應用程式的完整執行個體。然後使用 Cypress、Playwright 或 Selenium 等工具,透過模擬使用者行為來驗證使用者流程。

測試完整的應用程式會帶來權衡取捨
表面上,E2E 和互動測試看起來非常相似。但請記住,您的使用者與整個應用程式互動,而不僅僅是 UI 元件。E2E 測試在應用程式層級執行,這使其能夠發現前端和後端之間的整合問題。但這也需要您維護更多技術堆疊層級的測試基礎架構 (非常耗時!)。
元件層級測試由獨立工具完成,這些工具可以掛載、渲染和測試元件。使用 E2E 測試,您有責任啟動應用程式。為此,您有兩個選項
- 維護完整的測試環境:這包括前端、後端、服務和已植入的測試資料。例如,O'Reilly 團隊使用 Docker 來啟動其整個應用程式基礎架構並執行 E2E 測試。
- 維護僅限前端的測試環境,並搭配模擬後端。例如,Twilio 透過使用 Cypress 存根化網路請求來測試流程。
無論哪種方式,複雜性都會隨著系統規模的擴大而增加。系統越大,在持續整合伺服器上複製設定,然後連線到雲端瀏覽器以執行測試就越麻煩。
鑑於這種權衡取捨,大多數團隊使用混合方法來平衡精力和價值。E2E 測試僅限於關鍵使用者流程,而互動測試則用於驗證所有其他行為。
在本教學中,我們使用 Cypress 和模擬後端方法進行 E2E 測試。以下是工作流程摘要
- ⚙️ 設定:啟動應用程式並模擬網路請求 (重複使用來自 stories 的資料)
- 🤖 動作:使用 Cypress 瀏覽頁面並模擬互動
- ✅ 執行斷言以驗證 UI 是否已正確更新
教學:測試驗證流程
我們將為驗證流程編寫 E2E 測試:導覽至登入頁面並填寫使用者憑證。驗證成功後,使用者應該能夠看到其任務清單。


執行 yarn dev
以在開發模式下啟動應用程式。然後開啟 https://127.0.0.1:5173,您將看到登入畫面。
設定 Cypress
執行 yarn add --dev cypress
以安裝 Cypress 套件。然後將 Cypress 命令新增至 package.json
檔案的 scripts 欄位。
{
"scripts": {
"cypress": "cypress open"
}
}
接下來,在專案的根目錄新增 cypress.config.js
檔案。在這裡,我們可以設定應用程式的基本 URL,這樣在撰寫實際測試命令時就不必重複自己。
import { defineConfig } from 'cypress';
export default defineConfig({
e2e: {
baseUrl: 'https://127.0.0.1:5173/',
supportFile: false,
},
});
最後,執行 yarn cypress
以完成設定程序。這會將 cypress
資料夾新增至您的專案。所有測試檔案都將放在這裡。它也會啟動 Cypress 測試執行器。
測試驗證流程
Cypress 測試結構與您可能熟悉的其他測試類型非常相似。您首先描述要測試的內容。每個測試都放在一個 it
區塊中,您可以在其中執行斷言。以下是驗證使用者流程測試的外觀
describe('The Login Page', () => {
it('user can authenticate using the login form', () => {
const email = 'alice.carr@test.com';
const password = 'k12h1k0$5;lpa@Afn';
cy.visit('/');
// Fill out the form
cy.get('input[name=email]').type(email);
cy.get('input[name=password]').type(`${password}`);
// Click the sign-in button
cy.get('button[type=submit]').click();
// UI should display the user's task list
cy.get('[aria-label="tasks"] div').should("have.length", 6);
});
});
讓我們分解一下這裡發生的事情。cy.visit
將瀏覽器開啟至應用程式的登入頁面。然後我們使用 cy.get
命令來尋找並填寫電子郵件和密碼欄位。最後,按一下送出按鈕以實際登入。
測試的最後一部分執行斷言。換句話說,我們驗證驗證是否成功。我們透過檢查任務清單現在是否可見來執行此操作。
切換到 Cypress 視窗,您應該會看到測試已執行。
但是,請注意測試失敗了。那是因為我們只執行應用程式的前端。由於我們沒有作用中的後端,因此所有 HTTP 請求都會失敗。我們將使用存根化的網路請求,而不是啟動實際的後端。
模擬請求
cy.intercept
方法允許我們攔截網路請求並使用模擬資料回應。驗證使用者流程依賴於兩個請求:/authenticate
用於登入,以及 /tasks
用於擷取使用者的任務。為了存根化這些請求,我們需要一些模擬資料。
在組合測試章節中,我們為任務清單 stories 建立了模擬資料。我們現在將在 Cypress 測試中重複使用它。
import TaskList from './TaskList';
import * as TaskStories from './Task.stories';
export default {
component: TaskList,
title: 'TaskList',
argTypes: {
...TaskStories.argTypes,
},
};
export const Default = {
args: {
tasks: [
{ id: '1', state: 'TASK_INBOX', title: 'Build a date picker' },
{ id: '2', state: 'TASK_INBOX', title: 'QA dropdown' },
{
id: '3',
state: 'TASK_INBOX',
title: 'Write a schema for account avatar component',
},
{ id: '4', state: 'TASK_INBOX', title: 'Export logo' },
{ id: '5', state: 'TASK_INBOX', title: 'Fix bug in input error state' },
{
id: '6',
state: 'TASK_INBOX',
title: 'Draft monthly blog to customers',
},
],
},
};
讓我們繼續更新測試以模擬這兩個網路請求。
+ import { Default as TaskListDefault } from '../../src/components/TaskList.stories';
describe('The Login Page', () => {
+ beforeEach(() => {
+ cy.intercept('POST', '/authenticate', {
+ statusCode: 201,
+ body: {
+ user: {
+ name: 'Alice Carr',
+ token: 'eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9',
+ },
+ },
+ });
+
+ cy.intercept('GET', '/tasks', {
+ statusCode: 201,
+ body: TaskListDefault.args,
+ });
+ });
it('user can authenticate using the login form', () => {
const email = 'alice.carr@test.com';
const password = 'k12h1k0$5;lpa@Afn';
cy.visit('/');
// Fill out the form
cy.get('input[name=email]').type(email);
cy.get('input[name=password]').type(`${password}`);
// Click the sign-in button
cy.get('button[type=submit]').click();
// UI should display the user's task list
cy.get('[aria-label="tasks"] div').should("have.length", 6);
});
});
重新執行測試,現在應該會通過了。
我們啟動了整個應用程式,並模擬了使用者行為,並使用 Cypress 測試了登入流程。在這一個測試中,我們檢查了資料流程、表單提交和 API 呼叫。
自動化 UI 測試
只有在您持續執行測試時,測試才會有幫助。領先的工程團隊使用持續整合 (CI) 伺服器來執行其完整的測試套件,在每次程式碼推送時自動執行。我們已經介紹了五種不同的測試類型,下一章將向您展示如何自動化它們的執行。