在一个 Vue 项目中,如何集成和配置 `Cypress` 或 `Playwright` 等 E2E 测试框架,并编写高质量的测试用例?

大家好!今天咱们来聊聊 Vue 项目里的 E2E 测试,也就是端到端测试。说白了,就是模拟用户真实操作,看看咱们的网站是不是真的能按预期工作。 就像咱们开发完一个新功能,不能光靠自己点几下就觉得没问题了,得让机器也来点几下,而且要点得更狠,更全面,才能保证用户用起来舒心。

咱们今天主要讲两个比较流行的 E2E 测试框架:Cypress 和 Playwright。这两个家伙都是前端测试界的扛把子,各有千秋,咱们一个个来扒。

第一部分:Cypress – 调试利器,快如闪电

Cypress 的特点是上手简单,调试方便,而且跑得飞快。它直接在浏览器里运行,可以实时看到测试步骤,哪里错了也能立刻定位。

1. 安装 Cypress

首先,在你的 Vue 项目里安装 Cypress:

npm install cypress --save-dev
# 或者
yarn add cypress --dev

安装好之后,运行 Cypress:

npx cypress open
# 或者
yarn cypress open

这会打开 Cypress 的图形界面,第一次运行会生成一些默认的文件夹和文件,像 cypress.config.jscypress/integration,等等。

2. 配置 Cypress

Cypress 的配置文件是 cypress.config.js (或者 cypress.config.ts,看你喜欢)。 咱们可以在这里配置一些全局的选项,比如 baseUrl,viewport 大小,等等。

const { defineConfig } = require("cypress");

module.exports = defineConfig({
  e2e: {
    baseUrl: 'http://localhost:8080', // 假设你的 Vue 项目跑在 8080 端口
    viewportWidth: 1280,
    viewportHeight: 720,
    setupNodeEvents(on, config) {
      // implement node event listeners here
    },
  },
});
  • baseUrl: 测试的根 URL,这样在测试用例里就可以用相对路径访问页面了。
  • viewportWidthviewportHeight: 设置浏览器窗口大小,模拟不同设备的屏幕。
  • setupNodeEvents: 允许你在 Node.js 环境中执行一些任务,比如读取环境变量,连接数据库等等。

3. 编写你的第一个 Cypress 测试

cypress/integration 文件夹下新建一个文件,比如 example.cy.js(Cypress 10 之后推荐使用 .cy.js 后缀)。

describe('My First Test', () => {
  it('Visits the app root url', () => {
    cy.visit('/')
    cy.contains('h1', 'Welcome to Your Vue.js App')
  })
})
  • describe: 用来组织测试用例,可以理解为一个测试套件。
  • it: 一个具体的测试用例。
  • cy.visit(): 访问指定的 URL。
  • cy.contains(): 断言页面上是否存在包含指定文本的元素。 第一个参数是选择器,第二个参数是要包含的文本。

运行这个测试,Cypress 会自动打开浏览器,访问你的 Vue 应用,并检查页面上是否存在包含 "Welcome to Your Vue.js App" 文本的 h1 标签。

4. 更多 Cypress 命令

Cypress 提供了丰富的命令来模拟用户操作和进行断言。 下面是一些常用的命令:

命令 描述 示例
cy.get() 根据选择器获取元素。 cy.get('.my-button')
cy.click() 点击元素。 cy.get('.my-button').click()
cy.type() 在输入框中输入文本。 cy.get('#my-input').type('Hello World')
cy.clear() 清空输入框。 cy.get('#my-input').clear()
cy.submit() 提交表单。 cy.get('form').submit()
cy.select() 选择下拉框中的选项。 cy.get('#my-select').select('Option 2')
cy.check() 选中复选框或单选框。 cy.get('#my-checkbox').check()
cy.uncheck() 取消选中复选框。 cy.get('#my-checkbox').uncheck()
cy.should() 进行断言。 cy.get('.my-element').should('be.visible')
cy.request() 发送 HTTP 请求。 cy.request('/api/users')
cy.intercept() 拦截 HTTP 请求,可以用来 mock 数据。 cy.intercept('GET', '/api/users', { fixture: 'users.json' })
cy.wait() 等待指定的时间,或者等待某个别名(alias)的请求完成。 cy.wait(1000) cy.wait('@getUsers')

5. 一个更复杂的 Cypress 测试用例

假设咱们有一个简单的 Vue 组件,包含一个输入框和一个按钮。 点击按钮后,会显示输入框中的内容。

<template>
  <div>
    <input type="text" v-model="message" id="my-input">
    <button @click="showMessage" id="my-button">Show Message</button>
    <p id="message">{{ displayedMessage }}</p>
  </div>
</template>

<script>
export default {
  data() {
    return {
      message: '',
      displayedMessage: ''
    };
  },
  methods: {
    showMessage() {
      this.displayedMessage = this.message;
    }
  }
};
</script>

对应的 Cypress 测试用例:

describe('Input and Button Test', () => {
  it('Types a message and displays it when the button is clicked', () => {
    cy.visit('/')
    cy.get('#my-input').type('Hello Cypress!')
    cy.get('#my-button').click()
    cy.get('#message').should('contain', 'Hello Cypress!')
  })
})

6. Mocking API Requests

在 E2E 测试中,经常需要 mock API 请求,避免依赖真实的后端服务。 Cypress 提供了 cy.intercept() 命令来实现这个功能。

假设咱们的应用会从 /api/users 获取用户列表,咱们可以用 cy.intercept() 来 mock 这个请求:

describe('Mock API Test', () => {
  it('Mocks the /api/users endpoint', () => {
    cy.intercept('GET', '/api/users', {
      fixture: 'users.json' // 从 cypress/fixtures/users.json 文件读取数据
    }).as('getUsers') // 给这个请求起一个别名

    cy.visit('/')

    cy.wait('@getUsers') // 等待请求完成

    cy.get('.user-list').should('have.length', 3) // 假设 users.json 中有 3 个用户
  })
})

需要在 cypress/fixtures 目录下创建一个 users.json 文件,包含 mock 数据:

[
  { "id": 1, "name": "Alice" },
  { "id": 2, "name": "Bob" },
  { "id": 3, "name": "Charlie" }
]

第二部分:Playwright – 跨浏览器,功能强大

Playwright 另一个 E2E 测试框架,它支持多种浏览器(Chromium, Firefox, WebKit),可以模拟各种用户场景,比如文件上传,下载,地理位置模拟等等。

1. 安装 Playwright

npm install -D @playwright/test
# 或者
yarn add -D @playwright/test

安装好之后,运行 Playwright 初始化命令:

npx playwright install
# 或者
yarn playwright install

这会安装 Playwright 支持的浏览器。

2. 配置 Playwright

Playwright 的配置文件是 playwright.config.js (或者 playwright.config.ts)。

const { defineConfig, devices } = require('@playwright/test');

/**
 * @see https://playwright.dev/docs/test-configuration
 */
module.exports = defineConfig({
  testDir: './tests', // 测试文件存放的目录
  /* Run tests in files in parallel */
  fullyParallel: true,
  /* Fail the build on CI if you accidentally left test.only in the code. */
  forbidOnly: !!process.env.CI,
  /* Retry on CI only */
  retries: process.env.CI ? 2 : 0,
  /* Opt out of parallel tests on CI. */
  workers: process.env.CI ? 1 : undefined,
  /* Reporter to use. See https://playwright.dev/docs/test-reporters */
  reporter: 'html',
  /* Shared settings for all the projects below. See https://playwright.dev/docs/api/class-testoptions. */
  use: {
    /* Base URL to use in actions like `await page.goto('/')`. */
    baseURL: 'http://localhost:8080',

    /* Collect trace when retries are configured. See https://playwright.dev/docs/trace-viewer */
    trace: 'on-first-retry',
  },

  /* Configure projects for major browsers */
  projects: [
    {
      name: 'chromium',
      use: { ...devices['Desktop Chrome'] },
    },

    {
      name: 'firefox',
      use: { ...devices['Desktop Firefox'] },
    },

    {
      name: 'webkit',
      use: { ...devices['Desktop Safari'] },
    },
  ],

  /* Run your local dev server before starting the tests */
  // webServer: {
  //   command: 'npm run serve',
  //   port: 8080,
  // },
});
  • testDir: 测试文件存放的目录。
  • fullyParallel: 是否并行运行测试。
  • forbidOnly: 禁止在 CI 环境中使用 test.only
  • retries: 测试失败后重试的次数。
  • workers: 并发运行的 worker 数量。
  • reporter: 测试报告的格式。
  • use: 配置测试选项,比如 baseUrl,trace 等。
  • projects: 配置不同的浏览器。
  • webServer: 启动本地开发服务器。

3. 编写你的第一个 Playwright 测试

tests 目录下新建一个文件,比如 example.spec.js (或者 example.spec.ts)。

const { test, expect } = require('@playwright/test');

test('Visits the app root url', async ({ page }) => {
  await page.goto('/');
  await expect(page.locator('h1')).toContainText('Welcome to Your Vue.js App');
});
  • test: 定义一个测试用例。
  • expect: 进行断言。
  • page: Playwright 提供的页面对象,可以用来操作浏览器。
  • page.goto(): 访问指定的 URL。
  • page.locator(): 根据选择器获取元素。
  • toContainText(): 断言元素包含指定的文本。

运行这个测试:

npx playwright test
# 或者
yarn playwright test

4. 更多 Playwright 命令

命令 描述 示例
page.locator() 根据选择器获取元素。 page.locator('.my-button')
page.click() 点击元素。 await page.locator('.my-button').click()
page.type() 在输入框中输入文本。 await page.locator('#my-input').type('Hello World')
page.fill() 填充输入框。 await page.locator('#my-input').fill('Hello World')
page.selectOption() 选择下拉框中的选项。 await page.locator('#my-select').selectOption('Option 2')
page.check() 选中复选框或单选框。 await page.locator('#my-checkbox').check()
page.uncheck() 取消选中复选框。 await page.locator('#my-checkbox').uncheck()
page.waitForSelector() 等待元素出现。 await page.waitForSelector('.my-element')
page.goto() 访问指定的 URL。 await page.goto('/login')
page.request() 发送 HTTP 请求。 await page.request('/api/users')
page.route() 拦截 HTTP 请求,可以用来 mock 数据。 await page.route('/api/users', async route => { ... })

5. 一个更复杂的 Playwright 测试用例

还是用上面的 Vue 组件,对应的 Playwright 测试用例:

const { test, expect } = require('@playwright/test');

test('Types a message and displays it when the button is clicked', async ({ page }) => {
  await page.goto('/');
  await page.locator('#my-input').type('Hello Playwright!');
  await page.locator('#my-button').click();
  await expect(page.locator('#message')).toContainText('Hello Playwright!');
});

6. Mocking API Requests

Playwright 提供了 page.route() 命令来 mock API 请求。

const { test, expect } = require('@playwright/test');

test('Mock API Test', async ({ page }) => {
  await page.route('/api/users', async route => {
    await route.fulfill({
      status: 200,
      contentType: 'application/json',
      body: JSON.stringify([
        { id: 1, name: 'Alice' },
        { id: 2, name: 'Bob' },
        { id: 3, name: 'Charlie' }
      ])
    });
  });

  await page.goto('/');

  await expect(page.locator('.user-list')).toHaveCount(3);
});

第三部分:编写高质量的 E2E 测试用例

无论使用 Cypress 还是 Playwright,编写高质量的 E2E 测试用例都很重要。 下面是一些建议:

  1. 测试用例要简洁明了:每个测试用例只测试一个功能点,避免测试用例过于复杂。
  2. 使用有意义的测试用例名称:测试用例的名称应该清晰地描述测试的目的。
  3. 使用数据驱动测试:对于相同的测试逻辑,可以使用不同的数据来测试,提高测试覆盖率。
  4. 避免硬编码:尽量使用变量或配置文件来存储测试数据,方便维护。
  5. 使用 Page Object Model (POM):将页面元素和操作封装成一个个 Page Object,提高代码复用性和可维护性。
  6. 使用合适的断言:选择合适的断言来验证测试结果,比如 should('be.visible')should('contain', 'text')toHaveCount(3) 等。
  7. 清理测试环境:在测试完成后,清理测试数据,避免影响其他测试用例。
  8. 关注用户体验:E2E 测试的目标是模拟用户真实操作,所以要关注用户体验,比如页面加载速度,交互流畅度等。

第四部分:Cypress vs Playwright

特性 Cypress Playwright
浏览器支持 Chromium (Chrome, Edge), Firefox, Electron Chromium, Firefox, WebKit (Safari), Electron
调试 非常方便,实时显示测试步骤 相对复杂,但提供了 Trace Viewer 工具
API 简单易用,学习曲线低 功能强大,API 丰富,学习曲线稍高
并行测试 需要付费才能支持真正的并行测试 默认支持并行测试
跨域 默认不允许跨域访问 默认允许跨域访问
Mocking cy.intercept() page.route()
适用场景 小型项目,需要快速上手,调试方便 大型项目,需要支持多种浏览器,功能强大的 API

总结

Cypress 和 Playwright 都是优秀的 E2E 测试框架,选择哪个取决于你的项目需求和个人偏好。Cypress 上手简单,调试方便,适合小型项目。Playwright 功能强大,支持多种浏览器,适合大型项目。

希望今天的讲座对你有所帮助! 记住,测试是保证软件质量的重要一环,不要偷懒哦!

发表回复

您的邮箱地址不会被公开。 必填项已用 * 标注