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