返回UI 测试手册
React
章节
  • 介绍
  • 视觉
  • 组合
  • 交互
  • 可访问性
  • 用户流程
  • 自动化
  • 工作流程
  • 结论

测试用户流程

验证您的 UI 端到端工作

在生产环境中调试是噩梦。您必须检查应用程序的每一层。是组件错误、事件触发错误、样式问题、应用状态问题,还是 API 损坏?可能是以上任何一种情况,您必须理清原因。

UI 帮助人们在多个页面上导航一系列步骤以完成他们的目标。到目前为止,我们已经了解了 Storybook 如何轻松隔离每个页面并在其上运行视觉可访问性交互测试。但是,要验证整个流程并捕获集成问题,您需要端到端 (E2E) UI 测试。

您的 UI 是否端到端工作?

用户流程不只包含一个组件,它们涉及多个组件协同工作。每次交互都会触发状态更新、路由更改和 API 调用,这些都会影响屏幕上呈现的内容。由于存在所有这些故障点,因此逐个进行质量检查可能很困难。

团队使用 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 在开发模式下启动应用程序。然后打开 http://localhost: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: 'http://localhost: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