大家好!今天咱们来聊聊 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.js
,cypress/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,这样在测试用例里就可以用相对路径访问页面了。viewportWidth
和viewportHeight
: 设置浏览器窗口大小,模拟不同设备的屏幕。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 测试用例都很重要。 下面是一些建议:
- 测试用例要简洁明了:每个测试用例只测试一个功能点,避免测试用例过于复杂。
- 使用有意义的测试用例名称:测试用例的名称应该清晰地描述测试的目的。
- 使用数据驱动测试:对于相同的测试逻辑,可以使用不同的数据来测试,提高测试覆盖率。
- 避免硬编码:尽量使用变量或配置文件来存储测试数据,方便维护。
- 使用 Page Object Model (POM):将页面元素和操作封装成一个个 Page Object,提高代码复用性和可维护性。
- 使用合适的断言:选择合适的断言来验证测试结果,比如
should('be.visible')
,should('contain', 'text')
,toHaveCount(3)
等。 - 清理测试环境:在测试完成后,清理测试数据,避免影响其他测试用例。
- 关注用户体验: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 功能强大,支持多种浏览器,适合大型项目。
希望今天的讲座对你有所帮助! 记住,测试是保证软件质量的重要一环,不要偷懒哦!