Python脚本测试数据工厂如何构建

wen 实用脚本 1

本文目录导读:

Python脚本测试数据工厂如何构建

  1. 基础数据工厂模式
  2. 使用Faker库生成真实数据
  3. 数据工厂参数化
  4. 关联数据工厂
  5. 测试专用数据工厂
  6. 高级:使用Pydantic验证
  7. 最佳实践

构建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
        }

构建测试数据工厂的关键点:

  1. 封装性:将数据创建逻辑统一管理
  2. 灵活性:支持参数覆盖和默认值
  3. 可复用性:一次定义,多处使用
  4. 可读性:清晰的接口和文档
  5. 可维护性:集中管理测试数据变更

根据项目复杂度选择合适的方法,简单的项目使用基础工厂即可,复杂项目考虑使用Builder模式或结合Pydantic进行数据验证。

抱歉,评论功能暂时关闭!