本文目录导读:

构建Python测试数据工厂(Test Data Factory)的核心思想是将测试数据的创建逻辑封装起来,提供灵活、可复用的数据生成方式,以下是几种常用的构建方法:
基础数据工厂模式
1 简单的函数工厂
def create_user(name="测试用户", age=25, email=None):
if email is None:
email = f"{name.lower()}@example.com"
return {
"name": name,
"age": age,
"email": email,
"is_active": True,
"created_at": "2024-01-01"
}
# 使用默认值
user1 = create_user()
# 覆盖特定字段
user2 = create_user(name="张三", age=30)
2 类工厂模式
class UserFactory:
@staticmethod
def create(name="测试用户", age=25, **kwargs):
user = {
"name": name,
"age": age,
"email": f"{name.lower()}@example.com",
"is_active": True,
"created_at": "2024-01-01"
}
user.update(kwargs) # 允许额外字段
return user
@staticmethod
def create_batch(count=5, **kwargs):
return [UserFactory.create(**kwargs) for _ in range(count)]
# 使用
user = UserFactory.create()
users = UserFactory.create_batch(count=3, age=20)
使用Faker库生成真实数据
from faker import Faker
from datetime import datetime, timedelta
import random
fake = Faker('zh_CN') # 中文数据
class UserFactory:
@staticmethod
def create(name=None, **kwargs):
user = {
"name": name or fake.name(),
"age": random.randint(18, 65),
"email": fake.email(),
"phone": fake.phone_number(),
"address": fake.address(),
"is_active": random.choice([True, False]),
"salary": round(random.uniform(5000, 50000), 2),
"join_date": fake.date_between(start_date='-2y', end_date='today')
}
user.update(kwargs)
return user
数据工厂参数化
1 支持条件生成
class OrderFactory:
@staticmethod
def create(
status="pending",
amount=None,
items=None,
discount=0
):
if amount is None:
amount = random.randint(100, 1000)
if items is None:
items = random.randint(1, 10)
if discount > 0:
amount = int(amount * (1 - discount))
# 根据状态设置不同的数据
if status == "cancelled":
is_active = False
cancel_reason = "客户取消"
else:
is_active = True
cancel_reason = None
return {
"order_id": fake.uuid4(),
"status": status,
"amount": amount,
"items_count": items,
"discount": discount,
"is_active": is_active,
"cancel_reason": cancel_reason,
"created_at": datetime.now()
}
2 构建器模式(Builder Pattern)
class UserBuilder:
def __init__(self):
self.data = {
"name": "默认用户",
"age": 25,
"email": "default@example.com",
"role": "user"
}
def with_name(self, name):
self.data["name"] = name
return self
def with_age(self, age):
self.data["age"] = age
return self
def as_admin(self):
self.data["role"] = "admin"
return self
def build(self):
return self.data.copy()
# 使用
user = UserBuilder()\
.with_name("管理员")\
.with_age(30)\
.as_admin()\
.build()
关联数据工厂
class OrderFactory:
@staticmethod
def create_with_items(user_data=None, item_count=3):
if user_data is None:
user_data = UserFactory.create()
items = []
for i in range(item_count):
items.append({
"product_id": fake.uuid4(),
"name": fake.word(),
"price": random.randint(10, 100),
"quantity": random.randint(1, 5),
"user_id": user_data["id"] if "id" in user_data else None
})
total = sum(item["price"] * item["quantity"] for item in items)
return {
"order_id": fake.uuid4(),
"user": user_data,
"items": items,
"total": total,
"created_at": datetime.now()
}
测试专用数据工厂
class TestUserFactory:
"""专门为测试用例设计的数据工厂"""
# 预定义测试场景
SCENARIOS = {
"valid_user": {
"username": "testuser",
"password": "ValidPass123!",
"email": "test@example.com"
},
"invalid_email": {
"username": "testuser",
"password": "ValidPass123!",
"email": "invalid-email"
},
"short_password": {
"username": "testuser",
"password": "123",
"email": "test@example.com"
}
}
@classmethod
def create_from_scenario(cls, scenario_name, **overrides):
if scenario_name not in cls.SCENARIOS:
raise ValueError(f"Unknown scenario: {scenario_name}")
data = cls.SCENARIOS[scenario_name].copy()
data.update(overrides)
return data
@classmethod
def create_null_values(cls):
"""创建包含空值的测试数据"""
return {
"username": None,
"password": "",
"email": None
}
高级:使用Pydantic验证
from pydantic import BaseModel, validator
from typing import Optional
import random
class UserData(BaseModel):
name: str
age: int
email: Optional[str] = None
salary: float
@validator('age')
def validate_age(cls, v):
if v < 0 or v > 150:
raise ValueError('Invalid age')
return v
class UserFactory:
@staticmethod
def create_valid(**kwargs) -> UserData:
"""创建有效数据"""
data = {
"name": "测试用户",
"age": 25,
"email": "test@example.com",
"salary": 10000.0
}
data.update(kwargs)
return UserData(**data)
@staticmethod
def create_random() -> UserData:
"""创建随机有效数据"""
return UserData(
name=fake.name(),
age=random.randint(18, 65),
email=fake.email(),
salary=round(random.uniform(5000, 50000), 2)
)
最佳实践
1 工厂配置管理
import os
from dotenv import load_dotenv
load_dotenv()
class DataFactory:
"""可配置的数据工厂"""
@staticmethod
def get_config():
return {
"default_locale": os.getenv("TEST_LOCALE", "zh_CN"),
"batch_size": int(os.getenv("TEST_BATCH_SIZE", "10")),
"use_random": os.getenv("TEST_USE_RANDOM", "true").lower() == "true"
}
@classmethod
def create_user(cls, **kwargs):
config = cls.get_config()
if config["use_random"] and not kwargs:
return UserFactory.create_random()
return UserFactory.create(**kwargs)
2 工厂组合使用
class TestDataComposer:
"""组合多个工厂生成完整测试场景"""
@staticmethod
def create_complete_order_scenario():
user = UserFactory.create()
products = ProductFactory.create_batch(3)
order = OrderFactory.create_with_items(
user_data=user,
items_data=products
)
return {
"user": user,
"products": products,
"order": order
}
构建测试数据工厂的关键点:
- 封装性:将数据创建逻辑统一管理
- 灵活性:支持参数覆盖和默认值
- 可复用性:一次定义,多处使用
- 可读性:清晰的接口和文档
- 可维护性:集中管理测试数据变更
根据项目复杂度选择合适的方法,简单的项目使用基础工厂即可,复杂项目考虑使用Builder模式或结合Pydantic进行数据验证。