Cypress 测试数据管理 #

什么是 Fixtures? #

Fixtures 是 Cypress 提供的测试数据管理机制,用于将测试数据与测试代码分离,使测试更加清晰和可维护。

text
┌─────────────────────────────────────────────────────────────┐
│                    Fixtures 的优势                           │
├─────────────────────────────────────────────────────────────┤
│                                                             │
│  ✅ 数据与代码分离                                           │
│  ✅ 数据可复用                                               │
│  ✅ 易于维护                                                 │
│  ✅ 支持多种格式                                             │
│  ✅ 便于团队协作                                             │
│                                                             │
└─────────────────────────────────────────────────────────────┘

Fixtures 目录结构 #

默认目录 #

text
cypress/
├── fixtures/                    # 默认 fixtures 目录
│   ├── users.json              # 用户数据
│   ├── products.json           # 产品数据
│   ├── orders.json             # 订单数据
│   └── profile.json            # 配置数据
├── e2e/
└── support/

自定义目录结构 #

text
cypress/
├── fixtures/
│   ├── api/                    # API 响应数据
│   │   ├── users/
│   │   │   ├── list.json
│   │   │   └── detail.json
│   │   ├── products/
│   │   │   └── list.json
│   │   └── errors/
│   │       ├── 404.json
│   │       └── 500.json
│   ├── forms/                  # 表单数据
│   │   ├── login.json
│   │   └── register.json
│   └── config/                 # 配置数据
│       ├── dev.json
│       └── staging.json

使用 Fixtures #

cy.fixture() #

javascript
// 加载 fixture 文件
cy.fixture('users').then((users) => {
  console.log(users);
});

// 等同于
cy.fixture('users.json').then((users) => {
  console.log(users);
});

在 intercept 中使用 #

javascript
// 方式一:直接使用
cy.intercept('GET', '/api/users', { fixture: 'users.json' });

// 方式二:加载后使用
cy.fixture('users.json').then((users) => {
  cy.intercept('GET', '/api/users', users);
});

// 方式三:动态修改
cy.fixture('users.json').then((users) => {
  users.push({ id: 3, name: 'New User' });
  cy.intercept('GET', '/api/users', users);
});

在测试中使用 #

javascript
describe('用户测试', () => {
  let users;

  before(() => {
    cy.fixture('users').then((data) => {
      users = data;
    });
  });

  it('使用 fixture 数据', () => {
    cy.visit('/users');
    cy.get('.user-item').should('have.length', users.length);
  });
});

数据文件格式 #

JSON 格式 #

json
// cypress/fixtures/users.json
[
  {
    "id": 1,
    "name": "John Doe",
    "email": "john@example.com",
    "role": "admin"
  },
  {
    "id": 2,
    "name": "Jane Smith",
    "email": "jane@example.com",
    "role": "user"
  }
]

嵌套 JSON #

json
// cypress/fixtures/api/users/detail.json
{
  "user": {
    "id": 1,
    "profile": {
      "name": "John Doe",
      "avatar": "/images/avatar.jpg"
    },
    "settings": {
      "theme": "dark",
      "notifications": true
    }
  }
}

JavaScript 格式 #

javascript
// cypress/fixtures/users.js
module.exports = [
  {
    id: 1,
    name: 'User ' + Date.now(),
    createdAt: new Date().toISOString()
  }
];

CSV 格式 #

javascript
// 加载 CSV 文件
cy.fixture('data.csv').then((csvData) => {
  // 解析 CSV 数据
});

图片文件 #

javascript
// 加载图片
cy.fixture('images/logo.png', 'base64').then((logo) => {
  // logo 是 base64 编码的字符串
});

动态数据生成 #

使用函数生成 #

javascript
// cypress/fixtures/generators.js
const generateUser = (overrides = {}) => {
  return {
    id: Math.floor(Math.random() * 1000),
    name: `User ${Date.now()}`,
    email: `user${Date.now()}@example.com`,
    createdAt: new Date().toISOString(),
    ...overrides
  };
};

const generateUsers = (count) => {
  return Array.from({ length: count }, (_, i) => generateUser({ id: i + 1 }));
};

module.exports = {
  generateUser,
  generateUsers
};

使用 faker.js #

bash
npm install @faker-js/faker --save-dev
javascript
// cypress/support/data-generator.js
import { faker } from '@faker-js/faker';

export const generateUser = () => ({
  id: faker.number.int({ min: 1, max: 1000 }),
  name: faker.person.fullName(),
  email: faker.internet.email(),
  phone: faker.phone.number(),
  address: {
    street: faker.location.streetAddress(),
    city: faker.location.city(),
    country: faker.location.country()
  }
});

export const generateProduct = () => ({
  id: faker.number.int({ min: 1, max: 100 }),
  name: faker.commerce.productName(),
  price: parseFloat(faker.commerce.price()),
  description: faker.commerce.productDescription()
});

动态 fixture #

javascript
// cypress/fixtures/dynamic-users.js
const generateUsers = (count) => {
  return Array.from({ length: count }, (_, i) => ({
    id: i + 1,
    name: `User ${i + 1}`,
    email: `user${i + 1}@example.com`
  }));
};

module.exports = generateUsers(10);

数据复用 #

this 上下文 #

javascript
describe('用户测试', function() {
  beforeEach(function() {
    cy.fixture('users').as('users');
  });

  it('使用 this 访问 fixture', function() {
    cy.log(this.users[0].name);
    cy.visit('/users');
    cy.get('.user-name').first().should('contain', this.users[0].name);
  });
});

使用别名 #

javascript
describe('产品测试', () => {
  beforeEach(() => {
    cy.fixture('products').as('products');
    cy.fixture('categories').as('categories');
  });

  it('使用别名访问数据', function() {
    cy.get('@products').then((products) => {
      cy.visit('/products');
      cy.get('.product-item').should('have.length', products.length);
    });
  });
});

组合 Fixtures #

javascript
// 加载多个 fixture
beforeEach(() => {
  cy.fixture('users').as('users');
  cy.fixture('products').as('products');
  cy.fixture('orders').as('orders');
});

// 组合使用
it('组合多个 fixture 数据', function() {
  const order = {
    user: this.users[0],
    products: this.products.slice(0, 2),
    total: this.products[0].price + this.products[1].price
  };
  
  cy.intercept('POST', '/api/orders', order);
});

数据驱动测试 #

参数化测试 #

javascript
// cypress/fixtures/test-cases.json
[
  { "input": "valid@email.com", "expected": true },
  { "input": "invalid-email", "expected": false },
  { "input": "", "expected": false },
  { "input": "user@domain", "expected": false }
]
javascript
describe('邮箱验证测试', () => {
  beforeEach(() => {
    cy.fixture('test-cases').as('testCases');
  });

  it('验证各种邮箱格式', function() {
    this.testCases.forEach(({ input, expected }) => {
      cy.get('#email').clear().type(input);
      cy.get('.validate-button').click();
      
      if (expected) {
        cy.get('.success').should('be.visible');
      } else {
        cy.get('.error').should('be.visible');
      }
    });
  });
});

使用 test.each #

javascript
describe('计算器测试', () => {
  const testCases = [
    { a: 1, b: 2, expected: 3 },
    { a: 5, b: 3, expected: 8 },
    { a: -1, b: 1, expected: 0 }
  ];

  testCases.forEach(({ a, b, expected }) => {
    it(`${a} + ${b} = ${expected}`, () => {
      cy.get('#a').type(a);
      cy.get('#b').type(b);
      cy.get('.calculate').click();
      cy.get('#result').should('contain', expected);
    });
  });
});

环境特定数据 #

多环境配置 #

text
cypress/fixtures/
├── config/
│   ├── development.json
│   ├── staging.json
│   └── production.json
└── users.json
javascript
// cypress/support/e2e.js
before(() => {
  const env = Cypress.env('environment') || 'development';
  cy.fixture(`config/${env}`).as('config');
});

条件加载 #

javascript
const getFixture = (name) => {
  const env = Cypress.env('environment') || 'development';
  
  try {
    return cy.fixture(`${env}/${name}`);
  } catch {
    return cy.fixture(name);
  }
};

数据管理最佳实践 #

1. 合理组织目录结构 #

text
cypress/fixtures/
├── api/                    # API 响应
│   ├── users/
│   ├── products/
│   └── orders/
├── forms/                  # 表单数据
├── config/                 # 配置数据
└── test-data/              # 测试用例数据

2. 使用语义化命名 #

javascript
// ✅ 好的命名
users-list.json
user-detail.json
user-create-request.json
user-create-response.json

// ❌ 不好的命名
data1.json
temp.json
test.json

3. 保持数据最小化 #

json
// ✅ 好的做法 - 只包含必要字段
{
  "id": 1,
  "name": "John",
  "email": "john@example.com"
}

// ❌ 不好的做法 - 包含过多字段
{
  "id": 1,
  "name": "John",
  "email": "john@example.com",
  "phone": "1234567890",
  "address": "...",
  "preferences": "...",
  "metadata": "..."
}

4. 使用模板和工厂 #

javascript
// cypress/fixtures/templates/user.json
{
  "id": 1,
  "name": "Template User",
  "email": "template@example.com"
}

// 测试中使用
cy.fixture('templates/user').then((template) => {
  const user = {
    ...template,
    name: 'Custom Name',
    email: 'custom@example.com'
  };
});

完整示例 #

用户管理测试 #

javascript
// cypress/fixtures/users/list.json
[
  { "id": 1, "name": "John Doe", "email": "john@example.com", "role": "admin" },
  { "id": 2, "name": "Jane Smith", "email": "jane@example.com", "role": "user" },
  { "id": 3, "name": "Bob Wilson", "email": "bob@example.com", "role": "user" }
]

// cypress/fixtures/users/detail.json
{
  "id": 1,
  "name": "John Doe",
  "email": "john@example.com",
  "role": "admin",
  "profile": {
    "avatar": "/avatars/john.jpg",
    "bio": "Software Developer"
  }
}

// cypress/fixtures/users/create-request.json
{
  "name": "New User",
  "email": "newuser@example.com",
  "role": "user"
}

// cypress/fixtures/users/create-response.json
{
  "id": 4,
  "name": "New User",
  "email": "newuser@example.com",
  "role": "user",
  "createdAt": "2024-01-01T00:00:00Z"
}
javascript
// cypress/e2e/users.cy.js
describe('用户管理', () => {
  beforeEach(() => {
    cy.fixture('users/list').as('usersList');
    cy.fixture('users/detail').as('userDetail');
    cy.fixture('users/create-request').as('createRequest');
    cy.fixture('users/create-response').as('createResponse');
  });

  context('用户列表', () => {
    beforeEach(function() {
      cy.intercept('GET', '/api/users', this.usersList).as('getUsers');
      cy.visit('/users');
      cy.wait('@getUsers');
    });

    it('显示用户列表', function() {
      cy.get('.user-item').should('have.length', this.usersList.length);
    });

    it('显示用户详情', function() {
      cy.intercept('GET', '/api/users/1', this.userDetail).as('getUserDetail');
      
      cy.get('.user-item').first().click();
      cy.wait('@getUserDetail');
      
      cy.get('.user-detail').within(() => {
        cy.get('.name').should('contain', this.userDetail.name);
        cy.get('.email').should('contain', this.userDetail.email);
      });
    });
  });

  context('创建用户', () => {
    it('创建新用户', function() {
      cy.intercept('POST', '/api/users', this.createResponse).as('createUser');
      
      cy.visit('/users/new');
      cy.get('#name').type(this.createRequest.name);
      cy.get('#email').type(this.createRequest.email);
      cy.get('#role').select(this.createRequest.role);
      cy.get('.submit').click();
      
      cy.wait('@createUser').its('request.body').should('deep.equal', this.createRequest);
      cy.get('.success-message').should('be.visible');
    });
  });

  context('数据驱动测试', () => {
    const roles = ['admin', 'user', 'guest'];
    
    roles.forEach((role) => {
      it(`创建 ${role} 角色用户`, function() {
        const userData = {
          ...this.createRequest,
          role: role
        };
        
        cy.intercept('POST', '/api/users', {
          ...this.createResponse,
          role: role
        }).as('createUser');
        
        cy.visit('/users/new');
        cy.get('#name').type(userData.name);
        cy.get('#email').type(userData.email);
        cy.get('#role').select(userData.role);
        cy.get('.submit').click();
        
        cy.wait('@createUser');
      });
    });
  });
});

使用 Faker 生成数据 #

javascript
// cypress/support/data-generator.js
import { faker } from '@faker-js/faker';

export const createUser = (overrides = {}) => ({
  name: faker.person.fullName(),
  email: faker.internet.email(),
  phone: faker.phone.number(),
  ...overrides
});

export const createProduct = (overrides = {}) => ({
  name: faker.commerce.productName(),
  price: parseFloat(faker.commerce.price({ min: 10, max: 1000 })),
  description: faker.commerce.productDescription(),
  ...overrides
});

export const createOrder = (overrides = {}) => ({
  id: faker.number.int({ min: 1000, max: 9999 }),
  items: Array.from({ length: 3 }, () => createProduct()),
  total: parseFloat(faker.commerce.price({ min: 100, max: 500 })),
  ...overrides
});
javascript
// cypress/e2e/orders.cy.js
import { createUser, createProduct, createOrder } from '../support/data-generator';

describe('订单测试', () => {
  it('创建订单', () => {
    const user = createUser();
    const order = createOrder({ user });
    
    cy.intercept('POST', '/api/orders', order).as('createOrder');
    
    cy.visit('/orders/new');
    cy.get('#customer-name').type(user.name);
    cy.get('#customer-email').type(user.email);
    cy.get('.submit').click();
    
    cy.wait('@createOrder');
  });
});

下一步 #

现在你已经掌握了 Cypress 测试数据管理的方法,接下来学习 自定义命令 了解如何创建可复用的测试命令!

最后更新:2026-03-28