配置Pytest发现以忽略类名

22 投票
5 回答
10908 浏览
提问于 2025-04-18 12:22

Pytest 默认的查找规则会导入所有以 Test 开头的类,只要这些类没有 __init__() 方法。我遇到的情况是,这导致了一个错误的类被导入。

我正在测试一个使用 Factory Boy 的 Django 项目。http://factoryboy.readthedocs.org/en/latest/ 用来构建一个名为 Testimonial 的 Django 模型。

像这样:

class TestimonialFactory(factory.Factory):
    class Meta:
        model = models.Testimonial

问题在于 factory.Factory 没有 __init__() 方法。所以 py.test 看到 Testimonials 就试图运行它。这就导致在 pytest 查找阶段试图往数据库里插入记录(结果搞得一团糟,失败不断)。

我通过修改 pytest.ini 文件,强制查找以 Check 开头的测试类,而不是以 Test 开头的类,来绕过这个问题:

[pytest]
python_classes=Check

但这并不是我真正想要的。我想知道有没有办法明确告诉 py.test 忽略某个特定名称的测试?

5 个回答

2

配置选项似乎只能使用前缀或通配符,所以我觉得你不能排除特定的文件名:https://pytest.org/latest/customize.html#confval-python_classes

我觉得把你的类(MyTestimonialFactory)重命名,或者把它移到发现路径之外,会是个简单的解决办法。

不过,我还是觉得你可以使用某个收集钩子,在收集过程中跳过或移除那个类。也许可以用 pytest_pycollect_makeitem,具体可以参考这里:https://docs.pytest.org/en/latest/writing_plugins.html#collection-hooks

2

把所有的测试代码放到以 test_ 开头的文件里,然后把下面这段内容加到你的 pytest.ini 文件中:

[pytest]
python_files=test_*.py

这样做是为了告诉 pytest 只去找那些以 test_*.py 命名的文件里的测试。

4

你应该使用pytest.ini文件,并通过通配符来匹配类名。

#pytest.ini file
[pytest]
python_classes = !Test

这样做会让pytest忽略所有以Test开头的测试类。

更多信息可以查看这里:https://docs.pytest.org/en/latest/example/pythoncollection.html#changing-naming-conventions

11

这是一个比较老的问题,但在StackOverflow上似乎是唯一相关的答案,所以我想在这里留下一个备用的解答,以备后用。

另一种解决方法是禁用所有基于类名的发现,只依赖子类的发现仅此而已。换句话说:

在你的配置文件中:(setup.cfgpytest.ini):

[pytest]
python_classes = 

在你的测试文件中:

class TestWillNotBeRun(object):
  # Not a subclass of unittest.TestCase, so not collected regardless of name
class TestWillBeRun(unittest.TestCase):
  # Still okay to use TestName convention for readability
  # It just doesn't actually do anything.
class StillGoingToBeRun(unittest.TestCase): 
  # Relying on subclassing means *all* subclasses will be picked up, regardless of name

这样做的一个好处是,你不需要更改你的非测试类的名称。对于一个对用户开放的库来说,不改名是有很好的理由的。此外,这样也不需要大规模重命名测试类(因为它们现在可以是任何名称)。最后,与基于名称的发现不同,非测试代码不太可能成为unittest.TestCase的子类。(当然,我相信总有例外。)

缺点是你必须确保所有的测试类都是unittest.TestCase的子类。对我所有的代码来说,这已经是事实,所以没有额外的成本。不过,这并不一定适用于所有情况。

27

这里有一个我常用的简单解决方案,不过它会有一些额外的开销。

class DisablePyTestCollectionMixin(object):
  __test__ = False

class TestimonialFactory(DisablePyTestCollectionMixin):
  pass

参考来源: https://github.com/pytest-dev/pytest/issues/1879

撰写回答