如何在 pytest 中通过组合多个 fixture 实现参数化测试
本文介绍一种简洁、可维护的方案使用 pytest.mark.parametrize 驱动测试逻辑结合 fixture 依赖链动态生成测试数据避免直接传递复杂对象从而安全复用 category、product 等多层 fixture 并为不同场景如 new/published 文章指定预期状态码。 本文介绍一种简洁、可维护的方案使用 pytest.mark.parametrize 驱动测试逻辑结合 fixture 依赖链动态生成测试数据避免直接传递复杂对象从而安全复用 category、product 等多层 fixture 并为不同场景如 new/published 文章指定预期状态码。在 pytest 中当测试需覆盖多种数据组合例如不同状态的文章且这些数据又依赖于共享的 fixture如 category → product时直接在 pytest.mark.parametrize 中引用 fixture 是不允许的——因为参数化发生在 fixture 初始化之前。若强行将 articles_new 和 articles_published_with_readers 作为参数传入会破坏 fixture 的依赖注入机制导致 category 或 product 未初始化而报错。? 正确解法是将“数据类型”而非“数据实例”作为参数化维度在测试函数体内按需调用轻量级工厂函数构造具体对象并复用已声明的 fixture 提供上下文如 category。这种方式既保持了 fixture 的可组合性与生命周期管理又实现了测试用例的清晰分离。以下是一个结构清晰、生产就绪的实现示例import pytestfrom django.urls import reverse# 假设已定义的 factory 函数实际项目中应导入或定义def create_article_new(category): 基于 category 构建 NEW 状态文章模拟 ArticleFactory.create return type(Article, (), {guid: guid-new-123, status: NEW})()def create_article_published_with_readers(category): 基于 category 构建 PUBLISHED 有读者权限的文章 return type(Article, (), {guid: guid-pub-456, status: PUBLISHED})()pytest.fixturedef product(): return {product_1: prod-A, product_2: prod-B}pytest.fixturedef category(product): return {category_1: fcat-{product[product_1]}, category_2: fcat-{product[product_2]}}pytest.fixturedef client(): # 模拟 Django 测试客户端 class MockClient: def force_login(self, user): pass def get(self, url, **kwargs): return type(Response, (), {status_code: 200})() return MockClient()pytest.fixturedef user(): return type(User, (), {})()# ? 核心参数化 article_type expected 状态码pytest.mark.parametrize( article_type, expected, [ pytest.param(new, 200, idnew_article_accessible), pytest.param(published, 403, idpublished_article_forbidden), ],)def test_get_article_permissions(client, user, category, article_type, expected): 验证不同状态文章的访问权限控制 - new 文章登录用户可访问200 - published 文章需额外权限当前用户无权访问403 client.force_login(user) # 按类型动态创建文章实例复用 category fixture if article_type new: article create_article_new(category) elif article_type published: article create_article_published_with_readers(category) else: raise ValueError(fUnsupported article_type: {article_type}) # 执行请求并断言 url reverse(get_article, kwargs{article_guid: article.guid}) response client.get(url) assert response.status_code expected, ( fExpected status {expected} for {article_type!r} article, fbut got {response.status_code} )? 关键设计说明 Tellers AI Tellers是一款自动视频编辑工具可以将文本、文章或故事转换为视频。