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

测试用户流程

验证您的 UI 端到端正常工作
此社区翻译尚未更新到最新的 Storybook 版本。请将英文指南中的更改应用到此翻译中,帮助我们更新它。 欢迎提交 Pull Request.

在生产环境中调试是一场噩梦。您必须检查应用程序的每一层。是组件 bug、事件失灵、样式问题、应用状态,还是损坏的 API?可能是以上任何一种情况,您都需要弄清楚原因。

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

您的 UI 是否可以端到端运行?

用户流程不限于单个组件,它们涉及多个组件协同工作。每次交互都会触发状态更新、路由更改和 API 调用,这些都会影响屏幕上渲染的内容。所有这些潜在的故障点都使得逐一进行质量保证变得困难。

团队使用 E2E 测试来确保用户体验按预期工作。要运行 E2E 测试,您首先需要启动应用程序的完整实例。然后使用 Cypress、Playwright 或 Selenium 等工具通过模拟用户行为来验证用户流程。

应用程序通过将组件连接到数据、业务逻辑和 API 来组装

测试完整应用程序伴随着权衡

表面上看,E2E 和交互测试似乎非常相似。但请记住,您的用户与整个应用程序交互,而不仅仅是 UI 组件。E2E 测试在应用程序级别运行,这使它们能够发现前端和后端之间的集成问题。但这同时也要求您维护更多技术栈层的测试基础设施(非常耗时!)。

组件级别的测试由独立的工具完成,这些工具可以挂载、渲染和测试组件。使用 E2E 测试,您负责启动应用程序。为此,您有两个选择:

  1. 维护完整的测试环境:这包括前端、后端、服务和已填充的测试数据。例如,O'Reilly 团队使用 Docker 来启动他们的整个应用程序基础设施并运行 E2E 测试。
  2. 维护仅前端的测试环境,并配有模拟后端。例如,Twilio 使用 Cypress 来模拟网络请求以进行流程测试。

无论哪种方式,随着系统规模的扩大,复杂性都会呈指数级增长。系统越大,在持续集成服务器上复制设置,然后连接到云浏览器运行测试就越麻烦。

考虑到这种权衡,大多数团队采用混合方法来平衡成本和收益。E2E 测试仅限于关键用户流程,而交互测试用于验证所有其他行为。

在本教程中,我们将使用 Cypress 和模拟后端方法进行 E2E 测试。以下是工作流程的摘要:

  1. ⚙️ 设置:启动应用程序并模拟网络请求(重用故事中的数据)
  2. 🤖 操作:使用 Cypress 访问页面并模拟交互
  3. 运行断言以验证 UI 是否已正确更新

教程:测试身份验证流程

我们将为身份验证流程编写一个 E2E 测试:导航到登录页面并填写用户凭据。身份验证成功后,用户应该能够看到他们的任务列表。

login page inbox page

运行 yarn dev 在开发模式下启动应用程序。然后打开https://:5173,您将看到登录屏幕。

设置 Cypress

运行 yarn add --dev cypress 安装 Cypress 包。然后将 Cypress 命令添加到 package.json 文件的 scripts 字段中。

{
  "scripts": {
    "cypress": "cypress open"
  }
}

接下来,在项目根目录中添加一个 cypress.config.js 文件。在这里,我们可以配置应用程序的 base URL,这样在编写实际测试命令时就不必重复输入。

复制
cypress.config.js
import { defineConfig } from 'cypress';

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

最后,运行 yarn cypress 完成设置过程。这将向您的项目中添加一个 cypress 文件夹。所有测试文件都将保存在这里。它还将启动 Cypress 测试运行器。

测试 auth 流程

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 方法允许我们拦截网络请求并用模拟数据响应。auth 用户流程依赖于两个请求:登录的 /authenticate 和获取用户任务的 /tasks。为了模拟这些请求,我们需要一些模拟数据。

组件测试章节中,我们为任务列表故事创建了模拟数据。我们现在将在 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。
这个免费指南有帮助吗?在 Twitter 上分享您的想法,并帮助其他开发者找到它。
下一章
自动化
加快您的工作流程,并交付更高质量的代码
✍️ 在 GitHub 上编辑 – PR 欢迎!
加入社区
7,424开发者及更多
原因为什么选择 Storybook组件驱动的 UI
文档指南教程更新日志遥测
社区插件参与进来博客
展示探索关于
开源软件
Storybook - Storybook 中文

特别感谢 Netlify CircleCI