返回UI 測試手冊
React
章節
  • 簡介
  • 視覺
  • 組合
  • 互動
  • 可及性
  • 使用者流程
  • 自動化
  • 工作流程
  • 結論

測試使用者流程

驗證您的 UI 端對端皆可運作

在生產環境中除錯是場惡夢。您必須檢查應用程式的每一層。是元件錯誤、事件觸發錯誤、樣式問題、應用程式狀態,還是 API 損壞?以上皆有可能,而您必須釐清原因。

UI 協助人們在多個頁面上瀏覽一連串步驟,以達成其目標。到目前為止,我們已了解 Storybook 如何輕鬆隔離每個頁面,並在這些頁面上執行視覺可及性互動測試。但是,為了驗證整個流程並找出整合問題,您需要端對端 (E2E) UI 測試。

您的 UI 是否端對端皆可運作?

使用者流程不只包含一個元件,它們牽涉到多個元件協同運作。每次互動都會觸發狀態更新、路由變更和 API 呼叫,這些都會影響螢幕上呈現的內容。由於有這麼多故障點,逐一進行 QA 可能會很困難。

團隊使用 E2E 測試來確保使用者體驗如預期般運作。若要執行 E2E 測試,您首先要啟動應用程式的完整執行個體。然後使用 CypressPlaywrightSelenium 等工具,透過模擬使用者行為來驗證使用者流程。

應用程式是透過將元件連接到資料、業務邏輯和 API 來組裝的

測試完整的應用程式會帶來權衡取捨

表面上,E2E 和互動測試看起來非常相似。但請記住,您的使用者與整個應用程式互動,而不僅僅是 UI 元件。E2E 測試在應用程式層級執行,這使其能夠發現前端和後端之間的整合問題。但這也需要您維護更多技術堆疊層級的測試基礎架構 (非常耗時!)。

元件層級測試由獨立工具完成,這些工具可以掛載、渲染和測試元件。使用 E2E 測試,您有責任啟動應用程式。為此,您有兩個選項

  1. 維護完整的測試環境:這包括前端、後端、服務和已植入的測試資料。例如,O'Reilly 團隊使用 Docker 來啟動其整個應用程式基礎架構並執行 E2E 測試。
  2. 維護僅限前端的測試環境,並搭配模擬後端。例如,Twilio 透過使用 Cypress 存根化網路請求來測試流程。

無論哪種方式,複雜性都會隨著系統規模的擴大而增加。系統越大,在持續整合伺服器上複製設定,然後連線到雲端瀏覽器以執行測試就越麻煩。

鑑於這種權衡取捨,大多數團隊使用混合方法來平衡精力和價值。E2E 測試僅限於關鍵使用者流程,而互動測試則用於驗證所有其他行為。

在本教學中,我們使用 Cypress 和模擬後端方法進行 E2E 測試。以下是工作流程摘要

  1. ⚙️ 設定:啟動應用程式並模擬網路請求 (重複使用來自 stories 的資料)
  2. 🤖 動作:使用 Cypress 瀏覽頁面並模擬互動
  3. 執行斷言以驗證 UI 是否已正確更新

教學:測試驗證流程

我們將為驗證流程編寫 E2E 測試:導覽至登入頁面並填寫使用者憑證。驗證成功後,使用者應該能夠看到其任務清單。

login page inbox page

執行 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,這樣在撰寫實際測試命令時就不必重複自己。

複製
cypress.config.js
import { defineConfig } from 'cypress';

export default defineConfig({
  e2e: {
    baseUrl: 'https://127.0.0.1:5173/',
    supportFile: false,
  },
});

最後,執行 yarn cypress 以完成設定程序。這會將 cypress 資料夾新增至您的專案。所有測試檔案都將放在這裡。它也會啟動 Cypress 測試執行器。

測試驗證流程

Cypress 測試結構與您可能熟悉的其他測試類型非常相似。您首先描述要測試的內容。每個測試都放在一個 it 區塊中,您可以在其中執行斷言。以下是驗證使用者流程測試的外觀

複製
cypress/e2e/auth.cy.js
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 測試中重複使用它。

複製
TaskList.stories.jsx
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',
      },
    ],
  },
};

讓我們繼續更新測試以模擬這兩個網路請求。

複製
cypress/e2e/auth.cy.js
+ 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) 伺服器來執行其完整的測試套件,在每次程式碼推送時自動執行。我們已經介紹了五種不同的測試類型,下一章將向您展示如何自動化它們的執行。

讓您的程式碼與本章節保持同步。在 GitHub 上檢視 74eeff9。
本免費指南對您有幫助嗎?發推文表示讚賞,並協助其他開發人員找到它。
下一章
自動化
加速您的工作流程並交付更高品質的程式碼
✍️ 在 GitHub 上編輯 – 歡迎 PR!
加入社群
6,721位開發人員及更多
為何為何選擇 Storybook元件驅動 UI
開放原始碼軟體
Storybook - Storybook 繁體中文

特別感謝 Netlify 以及 CircleCI