测试数据生成Skill test-data-generation

测试数据生成技能用于创建逼真、可复现的测试数据,以支持软件开发和测试过程中的数据需求。关键词包括:数据生成、数据工厂、数据库种子、数据匿名化、边界值测试。

测试 0 次安装 2 次浏览 更新于 2/25/2026

以下是对“test-data-generation”技能的中文翻译和描述:


name: 测试数据生成 description: 使用Faker.js和类似工具进行合成测试数据的生成和管理。生成逼真的测试数据,创建数据工厂,实现数据库种子数据填充,和管理测试数据匿名化。 allowed-tools: Bash(*) 读写编辑 Grep WebFetch metadata: author: babysitter-sdk version: “1.0.0” category: 测试数据管理 backlog-id: SK-015

测试数据生成

你是测试数据生成 - 一个专门用于合成测试数据生成和管理的专业技能,提供创建逼真、可复现的测试数据的能力。

概览

这项技能使得AI驱动的测试数据管理成为可能,包括:

  • 使用Faker.js生成逼真的测试数据
  • 创建数据工厂和构建器
  • 数据库种子脚本
  • 测试数据匿名化和掩码
  • 生成边界值测试数据
  • 配置数据清理策略
  • 使用种子创建确定性测试数据
  • 与ORM工厂集成(Fishery, Factory Bot)

先决条件

  • Node.js或Python环境
  • 安装Faker库(@faker-js/faker 或 faker-python)
  • 数据库访问权限,用于种子操作
  • 可选:ORM(Prisma, Sequelize, SQLAlchemy)用于工厂集成

能力

1. 基础数据生成

使用Faker.js生成逼真的测试数据:

import { faker } from '@faker-js/faker';

// 生成用户数据
const generateUser = () => ({
  id: faker.string.uuid(),
  email: faker.internet.email(),
  firstName: faker.person.firstName(),
  lastName: faker.person.lastName(),
  phone: faker.phone.number(),
  address: {
    street: faker.location.streetAddress(),
    city: faker.location.city(),
    state: faker.location.state(),
    zipCode: faker.location.zipCode(),
    country: faker.location.country()
  },
  company: faker.company.name(),
  jobTitle: faker.person.jobTitle(),
  avatar: faker.image.avatar(),
  createdAt: faker.date.past(),
  updatedAt: faker.date.recent()
});

// 生成多个用户
const users = faker.helpers.multiple(generateUser, { count: 100 });

2. 数据工厂模式

创建可重用的数据工厂:

import { faker } from '@faker-js/faker';

// 用户工厂
class UserFactory {
  static defaults = {
    id: () => faker.string.uuid(),
    email: () => faker.internet.email(),
    firstName: () => faker.person.firstName(),
    lastName: () => faker.person.lastName(),
    role: () => 'user',
    isActive: () => true,
    createdAt: () => faker.date.past()
  };

  static create(overrides = {}) {
    const defaults = Object.fromEntries(
      Object.entries(this.defaults).map(([key, fn]) => [key, fn()])
    );
    return { ...defaults, ...overrides };
  }

  static createMany(count, overrides = {}) {
    return Array.from({ length: count }, () => this.create(overrides));
  }

  // 特征方法
  static admin(overrides = {}) {
    return this.create({ role: 'admin', ...overrides });
  }

  static inactive(overrides = {}) {
    return this.create({ isActive: false, ...overrides });
  }
}

// 使用方法
const user = UserFactory.create();
const admin = UserFactory.admin({ firstName: 'Admin' });
const users = UserFactory.createMany(50);

3. Fishery工厂(TypeScript)

使用Fishery进行类型化工厂:

import { Factory } from 'fishery';
import { faker } from '@faker-js/faker';

interface User {
  id: string;
  email: string;
  firstName: string;
  lastName: string;
  role: 'user' | 'admin';
  profile: Profile;
}

interface Profile {
  bio: string;
  avatar: string;
}

const profileFactory = Factory.define<Profile>(() => ({
  bio: faker.person.bio(),
  avatar: faker.image.avatar()
}));

const userFactory = Factory.define<User>(({ associations, sequence }) => ({
  id: faker.string.uuid(),
  email: faker.internet.email(),
  firstName: faker.person.firstName(),
  lastName: faker.person.lastName(),
  role: 'user',
  profile: associations.profile || profileFactory.build()
}));

// 使用方法
const user = userFactory.build();
const admin = userFactory.build({ role: 'admin' });
const usersWithProfiles = userFactory.buildList(10, {}, {
  associations: { profile: profileFactory.build() }
});

4. 数据库种子填充

用测试数据填充数据库:

// seed.js - 数据库种子脚本
import { PrismaClient } from '@prisma/client';
import { faker } from '@faker-js/faker';

const prisma = new PrismaClient();

async function seed() {
  // 设置种子以复现结果
  faker.seed(12345);

  // 清除现有数据
  await prisma.order.deleteMany();
  await prisma.product.deleteMany();
  await prisma.user.deleteMany();

  // 创建用户
  const users = await Promise.all(
    Array.from({ length: 50 }, () =>
      prisma.user.create({
        data: {
          email: faker.internet.email(),
          name: faker.person.fullName(),
          password: faker.internet.password()
        }
      })
    )
  );

  // 创建产品
  const products = await Promise.all(
    Array.from({ length: 100 }, () =>
      prisma.product.create({
        data: {
          name: faker.commerce.productName(),
          description: faker.commerce.productDescription(),
          price: parseFloat(faker.commerce.price()),
          sku: faker.string.alphanumeric(8).toUpperCase(),
          inStock: faker.datatype.boolean()
        }
      })
    )
  );

  // 创建订单
  for (const user of users) {
    const orderCount = faker.number.int({ min: 1, max: 5 });
    for (let i = 0; i < orderCount; i++) {
      await prisma.order.create({
        data: {
          userId: user.id,
          status: faker.helpers.arrayElement(['pending', 'processing', 'shipped', 'delivered']),
          total: parseFloat(faker.commerce.price({ min: 10, max: 500 })),
          items: {
            create: faker.helpers.arrayElements(products, { min: 1, max: 5 }).map(p => ({
              productId: p.id,
              quantity: faker.number.int({ min: 1, max: 3 }),
              price: p.price
            }))
          }
        }
      });
    }
  }

  console.log('种子填充完成!');
}

seed()
  .catch(console.error)
  .finally(() => prisma.$disconnect());

5. 边界值生成

生成边缘情况测试数据:

import { faker } from '@faker-js/faker';

const boundaryValues = {
  // 字符串边界
  strings: {
    empty: '',
    singleChar: 'a',
    maxLength: 'a'.repeat(255),
    unicode: '日本語テスト',
    emoji: '🎉🚀💡',
    specialChars: '<script>alert("xss")</script>',
    sqlInjection: "'; DROP TABLE users; --",
    whitespace: '   spaces   ',
    newlines: 'line1
line2\rline3'
  },

  // 数字边界
  numbers: {
    zero: 0,
    negative: -1,
    maxInt: Number.MAX_SAFE_INTEGER,
    minInt: Number.MIN_SAFE_INTEGER,
    decimal: 0.1 + 0.2, // 著名的浮点问题
    infinity: Infinity,
    nan: NaN
  },

  // 日期边界
  dates: {
    epochStart: new Date(0),
    farPast: new Date('1900-01-01'),
    farFuture: new Date('2100-12-31'),
    leapYear: new Date('2024-02-29'),
    endOfMonth: new Date('2024-01-31'),
    timezoneEdge: new Date('2024-03-10T02:30:00') // 夏令时转换
  },

  // 数组边界
  arrays: {
    empty: [],
    single: [1],
    large: Array.from({ length: 10000 }, (_, i) => i)
  }
};

// 生成边界测试用例
function generateBoundaryTestCases(schema) {
  const testCases = [];

  for (const [field, config] of Object.entries(schema)) {
    if (config.type === 'string') {
      testCases.push(
        { [field]: '', expected: config.required ? 'error' : 'success' },
        { [field]: 'a'.repeat(config.maxLength + 1), expected: 'error' },
        { [field]: 'a'.repeat(config.maxLength), expected: 'success' }
      );
    }
    if (config.type === 'number') {
      testCases.push(
        { [field]: config.min - 1, expected: 'error' },
        { [field]: config.min, expected: 'success' },
        { [field]: config.max, expected: 'success' },
        { [field]: config.max + 1, expected: 'error' }
      );
    }
  }

  return testCases;
}

6. 数据匿名化

为测试匿名化生产数据:

import { faker } from '@faker-js/faker';
import crypto from 'crypto';

const anonymize = {
  // 一致匿名化(相同输入=相同输出)
  email: (email) => {
    const hash = crypto.createHash('md5').update(email).digest('hex').slice(0, 8);
    return `user_${hash}@example.com`;
  },

  // 全部替换
  name: () => faker.person.fullName(),

  // 部分掩码
  phone: (phone) => phone.replace(/\d(?=\d{4})/g, '*'),

  // 格式保留
  creditCard: (cc) => {
    const last4 = cc.slice(-4);
    return `****-****-****-${last4}`;
  },

  // 一致的假数据
  ssn: (ssn) => {
    faker.seed(crypto.createHash('md5').update(ssn).digest('hex'));
    return faker.string.numeric('###-##-####');
  },

  // 地址匿名化
  address: () => ({
    street: faker.location.streetAddress(),
    city: faker.location.city(),
    state: faker.location.state(),
    zip: faker.location.zipCode()
  })
};

// 匿名化数据集
function anonymizeDataset(records) {
  return records.map(record => ({
    ...record,
    email: anonymize.email(record.email),
    name: anonymize.name(),
    phone: anonymize.phone(record.phone),
    creditCard: record.creditCard ? anonymize.creditCard(record.creditCard) : null,
    address: anonymize.address()
  }));
}

7. 多语言支持

在不同语言环境中生成数据:

import { faker, Faker } from '@faker-js/faker';
import { de, fr, ja, es } from '@faker-js/faker';

// 德语环境
const fakerDE = new Faker({ locale: [de] });
const germanUser = {
  name: fakerDE.person.fullName(),
  address: fakerDE.location.streetAddress(),
  city: fakerDE.location.city()
};

// 日语环境
const fakerJA = new Faker({ locale: [ja] });
const japaneseUser = {
  name: fakerJA.person.fullName(),
  address: fakerJA.location.streetAddress(),
  city: fakerJA.location.city()
};

// 为多种语言环境生成测试数据
const locales = { de, fr, ja, es };
function generateMultiLocaleData(count = 10) {
  return Object.entries(locales).flatMap(([code, locale]) => {
    const localFaker = new Faker({ locale: [locale] });
    return Array.from({ length: count }, () => ({
      locale: code,
      name: localFaker.person.fullName(),
      email: localFaker.internet.email(),
      phone: localFaker.phone.number(),
      address: localFaker.location.streetAddress()
    }));
  });
}

8. 使用种子的确定性数据

创建可复现的测试数据:

import { faker } from '@faker-js/faker';

// 设置全局种子以复现
faker.seed(12345);

// 每次生成相同的数据
const user1 = faker.person.fullName(); // 总是相同的名字
const user2 = faker.person.fullName(); // 总是相同的名字

// 重置种子以获得新的序列
faker.seed(12345);
const user1Again = faker.person.fullName(); // 与user1相同

// 基于环境的种子设置
const testSeed = process.env.TEST_SEED || Date.now();
faker.seed(testSeed);
console.log(`使用种子: ${testSeed}`);

MCP服务器集成

这项技能可以利用以下MCP服务器增强能力:

服务器 描述 安装
funsjanssen/faker-mcp Faker.js MCP服务器 GitHub

最佳实践

  1. 使用种子 - 启用可复现的测试数据
  2. 工厂优于内联 - 使用工厂模式以维护性
  3. 逼真但安全 - 数据看起来真实但不应匹配真实人物
  4. 边界覆盖 - 在测试数据中包含边缘情况
  5. 清理 - 实施数据清理策略
  6. 性能 - 为大型数据集批量生成数据
  7. 验证 - 验证生成的数据符合预期模式

流程集成

这项技能与以下流程集成:

  • test-data-management.js - 测试数据管理的所有阶段
  • e2e-test-suite.js - E2E测试数据设置
  • api-testing.js - API测试数据生成
  • environment-management.js - 环境数据种子填充

输出格式

执行操作时,提供结构化输出:

{
  "operation": "generate",
  "dataType": "users",
  "count": 100,
  "seed": 12345,
  "locale": "en",
  "schema": {
    "id": "uuid",
    "email": "email",
    "name": "fullName"
  },
  "outputFile": "./test-data/users.json",
  "statistics": {
    "generated": 100,
    "uniqueEmails": 100,
    "executionTime": "45ms"
  }
}

错误处理

  • 生成前验证模式
  • 处理大型数据集的内存限制
  • 提供种子信息以调试
  • 记录生成失败的上下文
  • 支持部分数据生成恢复

约束

  • 切勿使用真实个人数据作为种子
  • 确保生成的电子邮件不匹配真实域名
  • 避免生成可能被视为真实凭据的数据
  • 遵守数据隐私法规(GDPR等)
  • 记录种子值以测试可复现性