应使用工厂函数而非直接实例化类,因其可绕过初始化副作用、控制对象状态、支持默认/随机/边界值,并避免typeerror和assertionerror;推荐make_前缀命名、none作默认值、懒构造关联对象、配合fixture分层使用,且需注意性能与默认值漂移风险。

为什么不用类直接实例化,而要写工厂函数
测试对象往往需要控制状态、绕过初始化副作用、复用固定数据结构——类的 __init__ 一执行就可能发 HTTP 请求、连数据库、校验字段,根本没法测。工厂函数把构造逻辑抽出来,能按需返回“干净”的对象,还能加默认值、随机值、边界值。
常见错误现象:TypeError: __init__() missing 1 required positional argument,其实是测试里硬 new 了类,但没传 mock 掉的依赖;或者 AssertionError 因为对象里某个字段总被 __post_init__ 或 property 动态算错。
- 工厂函数名建议带
make_前缀(如make_user()),和真实类名区分开,避免 IDE 自动补全混淆 - 默认参数全部设为
None,而不是""或0,否则无法区分“用户真传了空字符串”和“用户想用默认值” - 如果类用了
dataclass或pydantic.BaseModel,工厂里别直接改__dict__,优先走正常初始化路径 + 覆盖字段
工厂函数怎么处理可选依赖和嵌套对象
真实业务对象常含外键、关联模型、配置对象。测试时你既不想造一整套关系链,又不能让字段为 None 导致后续报 AttributeError。工厂函数得支持“懒构造”:只在需要时生成关联对象,并允许外部传入已存在的实例。
使用场景:测订单提交逻辑,需要 Order 带一个 User 和两个 Item,但你只想验证价格计算,不关心用户邮箱格式或商品库存扣减。
立即学习“Python免费学习笔记(深入)”;
本文档主要讲述的是maven使用方法;Maven是基于项目对象模型的(pom),可以通过一小段描述信息来管理项目的构建,报告和文档的软件项目管理工具。Maven将你的注意力从昨夜基层转移到项目管理层。Maven项目已经能够知道 如何构建和捆绑代码,运行测试,生成文档并宿主项目网页。希望本文档会给有需要的朋友带来帮助;感兴趣的朋友可以过来看看
- 对关联字段,参数命名为
user=None,内部判断:if user is None: user = make_user() - 嵌套列表字段(如
items)默认给空列表[],不自动造[make_item()],避免测试用例意外依赖“默认有一条商品” - 用
**kwargs接收未声明字段,透传给类初始化(MyModel(**kwargs)),方便临时覆盖深层字段,比如make_order(items=[make_item(price=999)])
工厂函数和 pytest fixtures 冲突吗
不冲突,但混用容易乱。fixtures 适合跨测试共享的“稳定上下文”(如一个已登录的测试用户),工厂函数适合单个测试内“按需定制”的对象(如不同 status 的订单)。强行把工厂逻辑塞进 fixture,会导致 fixture 变重、参数难管理、调试时不知道对象哪来的。
常见错误现象:写了个 @pytest.fixture 叫 user,结果所有测试都用同一个 id=1 的用户,某测试改了它的邮箱,下一个测试断言失败;或者 fixture 带参数但没声明 params,pytest 直接忽略参数。
- fixture 命名用名词(
admin_user),工厂函数用动词(make_user()),一眼分清用途 - fixture 里调工厂函数完全 OK,比如
return make_user(is_active=True),但别在 fixture 里做复杂条件分支 - 如果工厂函数要读配置(如 faker locale),别在函数体里 import faker,提成模块级变量,避免每次调用都初始化 faker 实例
性能和可维护性上最容易被忽略的点
工厂函数本身不慢,但滥用会拖慢整个测试套件:比如每个测试都调 make_order(),而它内部又调三次 make_item(),再各自调 make_category()……最后跑出 O(n³) 的对象树,启动时间翻倍。
更隐蔽的问题是“默认值漂移”:半年后有人改了 make_user() 的默认 email 从 "test@example.com" 换成 "demo@local",几十个测试突然邮箱校验失败,没人记得这个默认值在哪起作用。
- 在工厂函数顶部加简短 docstring,写明关键默认值,例如
"""Returns User with email='test@example.com', is_active=True.""" - 对高频使用的组合(如“已支付订单”),单独拆函数
make_paid_order(),而不是在测试里堆make_order(status="paid", items=[...]) - 用类型注解标注返回值,比如
def make_user() -> User:,mypy 能帮你 catch 到工厂返回了 dict 却当对象用的低级错误
默认值不是写一次就完事,它和测试断言耦合极深。改之前,先全局搜 make_user() 看哪些测试依赖了它的 email、created_at 或其他字段。









