【问题标题】:Isolating pytest tests from each other将 pytest 测试相互隔离
【发布时间】:2019-03-08 16:41:15
【问题描述】:

我正在开发一个快速发展的 Python 项目。最近,我们的测试套件开始有点难以管理。尽管看似隔离良好,但当它们所在的模块以错误的顺序执行时,一些测试会失败。

我发现了一些其他问题,但他们关心的是固定装置:

Pytest fixtures interfering with each other

test isolation between pytest-hypothesis runs

虽然我们也在使用固定装置,但我认为问题不在于它们,但更可能是因为我们使用的库中类的内部状态会因测试运行而改变,例如 @987654323 @。

我最初来自 Java 世界,除非你明确地让你的测试相互依赖或做一些疯狂和不寻常的事情,否则这种情况不会发生。在 Python 中遵循相同的一组实践导致我遇到了这些问题,因此我意识到我可能遗漏了 Python 测试开发的一些关键规则。

是否可以告诉pytest 在各个模块运行之间删除/恢复/重新初始化所有类的内部状态?

如果不是,我们应该在测试开发过程中遵循哪些规则来防止这些问题发生?

编辑:我们发现的问题之一是我们在测试文件的顶层设置了一些对象,这些对象是由mockito-python 创建的。执行测试时,首先将文件全部导入,然后执行测试。发生的事情是,一项测试调用了mockito.unstub(),这会破坏 mockito 的模拟注册表中的所有模拟。因此,当测试实际执行时,另一个测试已经拆除了它的模拟。这种行为非常违反直觉,尤其是对于没有经验的开发人员。

【问题讨论】:

  • 一般来说,如果你有可变的全局状态,这是一个不好的迹象,尽管有时这是必要的。如果您可以确定全局状态的位置,则可以在测试后使用固定装置进行隔离和拆除。 yield 固定装置是管理全局模拟的好方法,也可用于重置全局状态
  • 正如之前的评论所述,固定装置就是这里的答案。在固定装置中设置和拆除模拟。

标签: python unit-testing testing pytest fixtures


【解决方案1】:

在python中很容易发生某些状态被错误修改的情况。例如,在将列表分配给变量时,如果需要创建列表的副本,很容易忘记添加 [:]

x = [0,1,2,3,4,5]
y = x    # oops, should have been x[:]
y[2] = 7 # now we modify state somewhere...
x
=> [0, 1, 7, 3, 4, 5]

至少更有可能识别此类问题的一种可能方法是按随机顺序执行单元测试。我根据https://stackoverflow.com/a/4006044/5747415 的想法进行了一个实验:

import unittest
import random
def randcmp(_, x, y):
    return random.randrange(-1, 2)
unittest.TestLoader.sortTestMethodsUsing = randcmp

因此,测试的执行顺序在测试执行之间发生了变化。如果错误地你的测试碰巧有依赖关系,你也许可以这样算出来,因为某些执行顺序会导致失败。当然,你会从小规模开始(只执行少量测试),这样你就有机会更容易找到罪魁祸首。

也许值得一试……

【讨论】:

  • 从未想过以随机顺序运行测试。这是根除这些问题的好主意!
【解决方案2】:

是否可以告诉 pytest 在各个模块运行之间删除/恢复/重新初始化所有类的内部状态?

您可以通过对invalidate cached modulesreload imported modules 使用importlib 来实现此目的(从而刷新代码和状态)。

当代码需要在模块级别运行时,这可能很有用,例如在Flask 中,通常在main.py 的模块级别初始化app

from flask import Flask

app = Flask(__name__)

@app.route("/")
def hello_world():
    return "<p>Hello, World!</p>"

在这种情况下,无论何时导入 from main import ...,都会创建 app。您可能希望以不同的方式测试初始化​​app,因此在您的测试中您可以执行类似

def test_app():
    import main
    importlib.reload(main)  # reset module state
    # do something with main.app

这将在您的测试期间重置 main 模块的状态。

【讨论】:

    猜你喜欢
    • 2018-02-07
    • 2018-05-25
    • 1970-01-01
    • 1970-01-01
    • 2020-08-14
    • 2018-06-03
    • 1970-01-01
    • 2019-05-06
    • 1970-01-01
    相关资源
    最近更新 更多