在 PyTest 中,在两个测试之间重置全局变量

7

我有一个使用和修改全局变量的函数测试。我想确保我的全局变量在测试之间被重置。有什么技巧可以做到这一点吗?

main.py:

y = 0


def inc(x):
    # side effect
    global y
    y = y + 1
    return x + y + 1

test_main.py:

from main import inc


def test_answer():
    assert inc(3) == 5


def test_answer_again():
    assert inc(3) == 5

_________________________________________________________________________________________ test_answer_again __________________________________________________________________________________________

    def test_answer_again():
>       assert inc(3) == 5
E       assert 6 == 5
E        +  where 6 = inc(3)

test_main.py:8: AssertionError
====================================================================================== short test summary info =======================================================================================
FAILED test_main.py::test_answer_again - assert 6 == 5
==================================================================================== 1 failed, 1 passed in 0.01s =====================================================================================

3
全局可变变量被认为是不好的设计,这就是其中一个原因。但是如果你确实需要这样做,你的测试可以只需说import main,然后main.y = 0。或者在main中创建一个专用函数来完成这个任务。 - Thomas
这是否回答了你的问题?https://dev59.com/iXRC5IYBdhLWcg3wD8xV - Bloodbee
3个回答

4

以下是如何使用简单的固定装置来确保每个测试后y的值不会改变:

import pytest

import main

from main import inc


@pytest.fixture
def set_y(request):
    y_before = main.y
    y_value_to_set = getattr(request, 'param', y_before)  # optional parameter
    main.y = y_value_to_set
    yield  # allows us to have cleanup after the test
    main.y = y_before  # once test is done, revert value for next test


def test_answer(set_y):
    assert inc(3) == 5


def test_answer_again(set_y):
    assert inc(3) == 5


@pytest.mark.parametrize('set_y', [20], indirect=["set_y"])
def test_answer_with_specific_y(set_y):
    assert inc(3) == 25

如果您希望在每个测试中使用同一个fixture,可以将autouse=True添加到fixture中,这样就不需要在每个测试中显式地指定fixture,也可以避免由于未指定fixture而导致的错误:

import pytest

import main

from main import inc


@pytest.fixture(autouse=True)
def set_y(request):
    y_before = main.y
    y_value_to_set = getattr(request, 'param', y_before)  # optional parameter
    main.y = y_value_to_set
    yield  # allows us to have cleanup after the test
    main.y = y_before  # once test is done, revert value for next test


def test_answer():
    assert inc(3) == 5


def test_answer_again():
    assert inc(3) == 5


@pytest.mark.parametrize('set_y', [20], indirect=["set_y"])
def test_answer_with_specific_y():
    assert inc(3) == 25

0

虽然这是一段时间以前的事情了,但如果你和我一样有一个想要为测试目的覆盖的设置模块,你可以使用以下范例,在每次运行测试后从磁盘重新加载设置模块。

my_package/src/settings.py

any_variable = "rukwinden"

my_package/tests/tests_thing.py

from importlib import reload
from my_package import settings

@pytest.fixture(autouse=True)  # autouse will insert it into every test in this scope. You may not want that
def overwrite_config():
    """Overwrites global config and resets it after tests"""
    global settings
    settings.any_variable = "balhoofd"
    yield
    settings = reload(__import__("my_package").settings)

0
在更复杂的情况下,你可能会发现在每个测试之间重置整个Python模块是必要的。经过尝试了各种方法后,我找到了一个适合我的解决方案:
import sys
import pytest
import mypackage

@pytest.fixture(autouse=True)
def reload_package():
    global mypackage    # reach the global scope
    import mypackage    # reimport package every before test
    yield               # run test

    # delete all modules from package
    loaded_package_modules = [key for key, value in sys.modules.items() if "mypackage" in str(value)]
    for key in loaded_package_modules:
        del sys.modules[key]


def test_answer():
    ...
    assert ...


def test_answer_again():
    ...
    assert ...

最后三行非常重要,因为它确保每个测试都以一个干净的状态开始,并且之前的测试所做的任何更改都不会干扰后续的测试。

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接