Cypress 高级特性 #
会话管理 #
cy.session() #
cy.session() 用于缓存和复用登录状态,提高测试效率:
javascript
// 基本用法
Cypress.Commands.add('login', (username, password) => {
cy.session([username, password], () => {
cy.visit('/login');
cy.get('#username').type(username);
cy.get('#password').type(password);
cy.get('button[type="submit"]').click();
cy.url().should('include', '/dashboard');
});
});
// 使用
beforeEach(() => {
cy.login('testuser', 'password123');
});
会话验证 #
javascript
Cypress.Commands.add('login', (username, password) => {
cy.session([username, password], () => {
cy.request({
method: 'POST',
url: '/api/auth/login',
body: { username, password }
}).then((response) => {
window.localStorage.setItem('token', response.body.token);
});
}, {
// 验证会话是否有效
validate() {
cy.request({
url: '/api/auth/me',
headers: {
Authorization: `Bearer ${window.localStorage.getItem('token')}`
}
});
},
// 缓存跨 spec 文件
cacheAcrossSpecs: true
});
});
会话清除 #
javascript
// 清除特定会话
cy.session.clear('testuser,password123');
// 清除所有会话
cy.session.clearAll();
跨域测试 #
cy.origin() #
cy.origin() 用于测试跨域场景:
javascript
// 访问不同域的页面
describe('跨域测试', () => {
it('可以在不同域之间操作', () => {
cy.visit('https://example.com');
cy.origin('https://other-domain.com', () => {
cy.get('.element').click();
cy.url().should('include', 'other-domain.com');
});
});
});
传递数据 #
javascript
it('跨域传递数据', () => {
const userData = { name: 'John', email: 'john@example.com' };
cy.visit('https://app.example.com');
cy.origin('https://auth.example.com', { args: { userData } }, ({ userData }) => {
cy.get('#username').type(userData.name);
cy.get('#email').type(userData.email);
cy.get('button').click();
});
});
OAuth 登录测试 #
javascript
Cypress.Commands.add('loginWithOAuth', (provider) => {
cy.visit('/login');
cy.get(`[data-provider="${provider}"]`).click();
cy.origin('https://oauth-provider.com', () => {
cy.get('#username').type('testuser');
cy.get('#password').type('password');
cy.get('#authorize').click();
});
cy.url().should('include', '/dashboard');
});
视觉测试 #
截图对比 #
javascript
// 安装插件
// npm install --save-dev cypress-image-snapshot
// cypress/support/e2e.js
import { addMatchImageSnapshotCommand } from 'cypress-image-snapshot/command';
addMatchImageSnapshotCommand();
// 使用
describe('视觉测试', () => {
it('首页快照测试', () => {
cy.visit('/');
cy.matchImageSnapshot('homepage');
});
it('组件快照测试', () => {
cy.get('.header').matchImageSnapshot('header');
});
});
配置选项 #
javascript
cy.matchImageSnapshot('snapshot-name', {
failureThreshold: 0.03, // 允许 3% 的差异
failureThresholdType: 'percent',
customDiffConfig: { threshold: 0.1 },
capture: 'viewport' // 或 'fullPage', 'runner'
});
手动截图 #
javascript
// 全页面截图
cy.screenshot('full-page', { capture: 'fullPage' });
// 元素截图
cy.get('.component').screenshot('component');
// 配置选项
cy.screenshot('screenshot-name', {
blackout: ['.sensitive-data'], // 遮蔽敏感信息
capture: 'viewport',
clip: { x: 0, y: 0, width: 100, height: 100 },
disableTimersAndAnimations: true,
overwrite: false,
scale: false
});
性能测试 #
页面加载性能 #
javascript
describe('性能测试', () => {
it('页面加载时间', () => {
cy.visit('/', {
onBeforeLoad: (win) => {
win.performance.mark('start-loading');
},
onLoad: (win) => {
win.performance.mark('end-loading');
win.performance.measure('page-load', 'start-loading', 'end-loading');
const measure = win.performance.getEntriesByName('page-load')[0];
cy.log(`页面加载时间: ${measure.duration}ms`);
expect(measure.duration).to.be.lessThan(3000);
}
});
});
it('使用 lighthouse 测试', () => {
// 需要安装 cypress-audit 插件
cy.lighthouse({
performance: 85,
accessibility: 90,
'best-practices': 85,
seo: 80
});
});
});
API 响应时间 #
javascript
it('API 响应时间测试', () => {
cy.intercept('GET', '/api/users').as('getUsers');
cy.visit('/users');
cy.wait('@getUsers').then((interception) => {
const duration = interception.duration;
cy.log(`API 响应时间: ${duration}ms`);
expect(duration).to.be.lessThan(1000);
});
});
插件开发 #
创建插件 #
javascript
// cypress/plugins/index.js
module.exports = (on, config) => {
// 注册 task
on('task', {
log(message) {
console.log(message);
return null;
},
readFile(filename) {
return fs.readFileSync(filename, 'utf8');
},
writeFile({ filename, content }) {
fs.writeFileSync(filename, content);
return null;
}
});
// 注册 before:browser:launch
on('before:browser:launch', (browser, launchOptions) => {
if (browser.family === 'chromium') {
launchOptions.args.push('--disable-web-security');
}
return launchOptions;
});
};
使用 task #
javascript
// 在测试中使用
cy.task('log', 'Hello from test');
cy.task('readFile', 'data.txt').then((content) => {
cy.log(content);
});
cy.task('writeFile', {
filename: 'output.txt',
content: 'Test output'
});
配置修改 #
javascript
// cypress.config.js
const { defineConfig } = require('cypress');
module.exports = defineConfig({
e2e: {
setupNodeEvents(on, config) {
// 根据环境修改配置
if (config.env.environment === 'staging') {
config.baseUrl = 'https://staging.example.com';
}
// 注册插件
on('task', {
// ...
});
return config;
}
}
});
浏览器控制 #
浏览器事件 #
javascript
// 监听窗口事件
cy.window().then((win) => {
win.addEventListener('beforeunload', (e) => {
e.preventDefault();
});
});
// 触发窗口事件
cy.window().invoke('dispatchEvent', new Event('resize'));
控制台日志 #
javascript
// 捕获控制台日志
Cypress.on('window:before:load', (win) => {
cy.stub(win.console, 'log').as('consoleLog');
cy.stub(win.console, 'error').as('consoleError');
});
it('验证控制台输出', () => {
cy.visit('/');
cy.get('@consoleLog').should('be.calledWith', 'App loaded');
cy.get('@consoleError').should('not.be.called');
});
地理位置 #
javascript
// 模拟地理位置
cy.visit('/', {
onBeforeLoad(win) {
cy.stub(win.navigator.geolocation, 'getCurrentPosition').callsFake((cb) => {
cb({
coords: {
latitude: 39.9042,
longitude: 116.4074
}
});
});
}
});
文件处理 #
读取文件 #
javascript
// 读取 JSON 文件
cy.readFile('cypress/fixtures/data.json').then((data) => {
cy.log(data);
});
// 读取文本文件
cy.readFile('cypress/fixtures/data.txt').then((text) => {
cy.log(text);
});
写入文件 #
javascript
// 写入 JSON 文件
cy.writeFile('cypress/results/test-result.json', {
status: 'passed',
timestamp: new Date().toISOString()
});
// 写入文本文件
cy.writeFile('cypress/results/log.txt', 'Test completed\n');
// 追加内容
cy.writeFile('cypress/results/log.txt', 'Another line\n', { flag: 'a' });
文件上传 #
javascript
// 使用 cypress-file-upload 插件
cy.get('input[type="file"]').attachFile('test.pdf');
// 上传多个文件
cy.get('input[type="file"]').attachFile(['file1.pdf', 'file2.pdf']);
// 自定义文件内容
cy.get('input[type="file"]').attachFile({
fileContent: new Blob(['file content'], { type: 'text/plain' }),
fileName: 'custom.txt',
mimeType: 'text/plain'
});
环境和配置 #
动态配置 #
javascript
// cypress.config.js
const { defineConfig } = require('cypress');
module.exports = defineConfig({
e2e: {
setupNodeEvents(on, config) {
// 从环境变量获取配置
config.env.apiUrl = process.env.API_URL || 'http://localhost:3000';
// 根据标签过滤测试
if (config.env.tags) {
config.excludeSpecPattern = ['**/*.skip.cy.js'];
}
return config;
}
}
});
条件测试 #
javascript
// 根据环境跳过测试
describe('生产环境测试', { env: { environment: 'production' } }, () => {
it('只在生产环境运行', () => {
if (Cypress.env('environment') !== 'production') {
this.skip();
}
// 测试代码
});
});
调试技巧 #
调试命令 #
javascript
// 暂停执行
cy.pause();
// 打开调试器
cy.debug();
// 输出日志
cy.log('Debug message');
// 输出元素信息
cy.get('.element').then(($el) => {
cy.log($el.text());
cy.log($el.attr('class'));
});
错误追踪 #
javascript
// 捕获未处理的异常
Cypress.on('uncaught:exception', (err, runnable) => {
console.log('Uncaught exception:', err.message);
// 返回 false 防止测试失败
return false;
});
// 测试特定错误
it('处理应用错误', () => {
cy.on('uncaught:exception', (err) => {
expect(err.message).to.include('Expected error');
return false;
});
cy.visit('/error-page');
});
完整示例 #
完整的登录测试套件 #
javascript
// cypress/e2e/auth/login-advanced.cy.js
describe('高级登录测试', () => {
const testUser = {
username: 'testuser',
password: 'Password123!'
};
beforeEach(() => {
cy.session([testUser.username, testUser.password], () => {
cy.request({
method: 'POST',
url: '/api/auth/login',
body: testUser
}).then((response) => {
window.localStorage.setItem('token', response.body.token);
});
}, {
validate() {
cy.request({
url: '/api/auth/me',
headers: {
Authorization: `Bearer ${window.localStorage.getItem('token')}`
}
});
},
cacheAcrossSpecs: true
});
});
it('性能测试 - 登录响应时间', () => {
cy.intercept('POST', '/api/auth/login').as('login');
cy.visit('/login');
cy.get('#username').type(testUser.username);
cy.get('#password').type(testUser.password);
cy.get('button[type="submit"]').click();
cy.wait('@login').its('duration').should('be.lessThan', 2000);
});
it('视觉测试 - 登录页面', () => {
cy.visit('/login');
cy.matchImageSnapshot('login-page', {
failureThreshold: 0.01
});
});
it('OAuth 登录', () => {
cy.visit('/login');
cy.get('[data-provider="google"]').click();
cy.origin('https://accounts.google.com', () => {
cy.get('#identifierId').type('testuser@gmail.com');
cy.get('#identifierNext').click();
});
cy.url().should('include', '/dashboard');
});
it('多设备测试', () => {
cy.viewport('iphone-x');
cy.visit('/login');
cy.get('.mobile-menu').should('be.visible');
cy.viewport('macbook-15');
cy.get('.desktop-menu').should('be.visible');
});
});
下一步 #
现在你已经掌握了 Cypress 的高级特性,接下来学习 最佳实践 了解如何编写高质量的测试代码!
最后更新:2026-03-28