name: 测试数据策略 description: 规划全面的测试数据管理,包括合成数据生成、数据匿名化、版本控制和环境特定策略。 allowed-tools: Read, Write, Glob, Grep, Task, WebSearch, WebFetch
测试数据策略
何时使用此技能
使用此技能当:
- 测试数据策略任务 - 处理规划全面的测试数据管理,包括合成数据生成、数据匿名化、版本控制和环境特定策略
- 规划或设计 - 需要测试数据策略方法的指导
- 最佳实践 - 希望遵循既定模式和标准
概述
有效的测试数据管理确保测试在正确时间拥有正确数据,同时保护敏感信息并跨环境维护数据质量。
测试数据类型
| 类型 | 来源 | 使用场景 | 隐私风险 |
|---|---|---|---|
| 合成数据 | 生成 | 单元/集成测试 | 无 |
| 子集数据 | 生产样本 | 性能测试 | 中等 |
| 脱敏数据 | 匿名化生产 | 真实场景 | 低 |
| 生产克隆 | 完整副本 | 预生产验证 | 高 |
| 基线数据 | 策划参考 | 回归测试 | 低 |
测试数据策略模板
# 测试数据策略: [项目名称]
## 1. 数据需求
### 按测试级别
| 级别 | 数据来源 | 体积 | 刷新 |
|-------|-------------|--------|---------|
| 单元 | 合成 | 最小 | 按需 |
| 集成 | 合成/子集 | 中等 | 每次运行 |
| 系统 | 脱敏生产 | 真实 | 每周 |
| 性能 | 缩放合成 | 生产类似 | 每次发布 |
### 按功能区域
| 功能 | 关键数据 | 所需体积 | 敏感性 |
|---------|---------------|-----------------|-------------|
| 认证 | 用户账户 | 1000 | 高 |
| 支付 | 交易 | 10000 | 高 |
| 报告 | 历史数据 | 100万条记录 | 中等 |
## 2. 数据生成策略
### 合成数据工具
- **单元测试**: AutoFixture, Bogus
- **集成**: TestContainers + 种子
- **性能**: 批量生成器
### 生成规则
| 实体 | 关键字段 | 生成逻辑 |
|--------|------------|------------------|
| 用户 | 邮箱 | `{guid}@test.example.com` |
| 订单 | 金额 | `Random(1, 10000)` |
| 日期 | 时间戳 | `Random(now-1y, now)` |
## 3. 数据匿名化
### PII 字段
| 字段 | 原始 | 匿名化方法 |
|-------|----------|---------------------|
| 姓名 | John Smith | Faker 生成 |
| 邮箱 | john@acme.com | `hash@domain.test` |
| 电话 | 555-123-4567 | `555-xxx-xxxx` |
| SSN | 123-45-6789 | `xxx-xx-xxxx` |
| 地址 | 123 Main St | Faker 地址 |
| 出生日期 | 1985-03-15 | 随机天平移 |
### 匿名化规则
- 保持数据关系
- 维护引用完整性
- 保留统计属性
- 移除唯一标识符
## 4. 环境策略
### 开发环境
- 来源: 100% 合成
- 刷新: 按需
- 体积: 最小
### QA 环境
- 来源: 脱敏生产子集
- 刷新: 每周
- 体积: 生产的 10%
### 预生产环境
- 来源: 脱敏生产克隆
- 刷新: 每次发布前
- 体积: 生产的 100%
### 性能环境
- 来源: 缩放合成
- 刷新: 性能运行前
- 体积: 生产的 150%
## 5. 数据版本控制
### 基线管理
- 版本化基线数据集
- 跟踪数据模式变化
- 保持向后兼容性
- 文档化数据依赖
### 刷新程序
1. 触发: [手动/计划/事件]
2. 来源: [生产/备份/生成器]
3. 转换: [匿名化步骤]
4. 加载: [目标环境]
5. 验证: [验证检查]
## 6. 合规要求
### GDPR 合规
- [ ] 非生产环境中无真实欧盟公民数据
- [ ] 支持删除权
- [ ] 应用数据最小化
- [ ] 匿名化同意跟踪
### HIPAA 合规
- [ ] PHI 完全去标识化
- [ ] 应用安全港方法
- [ ] 维护审计日志
- [ ] 验证访问控制
合成数据生成 (.NET)
使用 Bogus
using Bogus;
public class TestDataGenerator
{
public static Faker<Customer> CustomerFaker => new Faker<Customer>()
.RuleFor(c => c.Id, f => f.Random.Guid())
.RuleFor(c => c.FirstName, f => f.Person.FirstName)
.RuleFor(c => c.LastName, f => f.Person.LastName)
.RuleFor(c => c.Email, (f, c) => f.Internet.Email(c.FirstName, c.LastName))
.RuleFor(c => c.Phone, f => f.Phone.PhoneNumber())
.RuleFor(c => c.DateOfBirth, f => f.Date.Past(50, DateTime.Now.AddYears(-18)))
.RuleFor(c => c.Address, f => new Address
{
Street = f.Address.StreetAddress(),
City = f.Address.City(),
State = f.Address.StateAbbr(),
Zip = f.Address.ZipCode()
});
public static Faker<Order> OrderFaker(Customer customer) => new Faker<Order>()
.RuleFor(o => o.Id, f => f.Random.Guid())
.RuleFor(o => o.CustomerId, customer.Id)
.RuleFor(o => o.OrderDate, f => f.Date.Recent(30))
.RuleFor(o => o.Total, f => f.Finance.Amount(10, 1000))
.RuleFor(o => o.Status, f => f.PickRandom<OrderStatus>());
}
使用 AutoFixture
using AutoFixture;
using AutoFixture.Xunit2;
public class CustomerTests
{
[Theory, AutoData]
public void CreateCustomer_WithValidData_Succeeds(Customer customer)
{
// AutoFixture 自动生成有效客户
var result = _service.Create(customer);
Assert.True(result.IsSuccess);
}
[Theory, AutoData]
public void ProcessOrder_CalculatesCorrectTotal(
[Frozen] Customer customer,
Order order,
List<OrderItem> items)
{
// Frozen 确保客户被重用
// 订单和项目自动生成
order.Items = items;
var total = _calculator.Calculate(order);
Assert.Equal(items.Sum(i => i.Quantity * i.Price), total);
}
}
播种测试数据库
public class TestDatabaseSeeder
{
public static async Task SeedAsync(AppDbContext context)
{
// 清除现有数据
await context.Database.ExecuteSqlRawAsync("DELETE FROM Orders");
await context.Database.ExecuteSqlRawAsync("DELETE FROM Customers");
// 生成测试数据
var customers = TestDataGenerator.CustomerFaker.Generate(100);
await context.Customers.AddRangeAsync(customers);
foreach (var customer in customers)
{
var orders = TestDataGenerator.OrderFaker(customer).Generate(5);
await context.Orders.AddRangeAsync(orders);
}
await context.SaveChangesAsync();
}
}
数据匿名化技术
| 技术 | 描述 | 使用场景 |
|---|---|---|
| 替换 | 用假数据替换 | 姓名、邮箱 |
| 混洗 | 列内重新排列 | 薪水、日期 |
| 掩码 | 部分隐藏 | SSN (xxx-xx-1234) |
| 泛化 | 降低精度 | 年龄范围、邮政编码前缀 |
| 置空 | 完全移除 | 不必要字段 |
| 令牌化 | 用令牌替换 | 交叉引用需求 |
| 哈希 | 单向转换 | 标识符 |
.NET 匿名化示例
public class DataAnonymizer
{
public Customer Anonymize(Customer source)
{
return new Customer
{
Id = source.Id, // 为关系保留
FirstName = _faker.Person.FirstName,
LastName = _faker.Person.LastName,
Email = $"{Guid.NewGuid():N}@test.example.com",
Phone = MaskPhone(source.Phone),
SSN = "xxx-xx-" + source.SSN.Substring(7, 4),
DateOfBirth = ShiftDate(source.DateOfBirth),
Address = new Address
{
Street = _faker.Address.StreetAddress(),
City = source.Address.City, // 保留地理信息
State = source.Address.State,
Zip = source.Address.Zip.Substring(0, 3) + "00"
}
};
}
private string MaskPhone(string phone)
{
// 保留区号,掩码其余部分
return Regex.Replace(phone, @"(\d{3})\d{3}(\d{4})", "$1-xxx-$2");
}
private DateTime ShiftDate(DateTime date)
{
// 在 ±30 天内随机平移
return date.AddDays(_random.Next(-30, 30));
}
}
测试数据模式
构建器模式
public class CustomerBuilder
{
private Customer _customer = new();
public CustomerBuilder WithName(string first, string last)
{
_customer.FirstName = first;
_customer.LastName = last;
return this;
}
public CustomerBuilder WithPremiumStatus()
{
_customer.IsPremium = true;
_customer.PremiumSince = DateTime.Now.AddYears(-1);
return this;
}
public CustomerBuilder WithOrders(int count)
{
_customer.Orders = TestDataGenerator.OrderFaker(_customer).Generate(count);
return this;
}
public Customer Build() => _customer;
}
// 用法
var customer = new CustomerBuilder()
.WithName("Test", "User")
.WithPremiumStatus()
.WithOrders(5)
.Build();
对象母版模式
public static class TestCustomers
{
public static Customer ValidCustomer() => new()
{
Id = Guid.NewGuid(),
FirstName = "Test",
LastName = "User",
Email = "test@example.com",
Status = CustomerStatus.Active
};
public static Customer PremiumCustomer() => new()
{
Id = Guid.NewGuid(),
FirstName = "Premium",
LastName = "User",
Email = "premium@example.com",
IsPremium = true,
Status = CustomerStatus.Active
};
public static Customer InactiveCustomer() => new()
{
Id = Guid.NewGuid(),
Status = CustomerStatus.Inactive
};
}
集成点
输入自:
- 数据模型 → 测试数据结构
- 隐私要求 → 匿名化规则
test-strategy-planning技能 → 数据体积需求
输出到:
- 测试自动化 → 数据夹具
performance-test-planning技能 → 负载数据- 环境配置 → 播种脚本