在 mock class 中使用 pytest fixture
Use pytest fixture inside mock class
我需要使用一个 fixture 在 class 中准备一些数据,这些数据将用于模拟第三方库。现在我有相当于这个:
@pytest.fixture(scope="session")
def file(tmpdir_factory):
"""Long process that creates a mock file."""
...
return file_path
我需要在 class 构造函数中使用这个固定装置,例如:
class Mock:
def __init__(self, file):
self._file = file
def get(self, *args, **kwargs):
return self._file
我需要 file
夹具在 Mock
class 之外,因为它在其他地方使用。 Mock
class 的用法与此非常相似:
def my_test():
with patch("thirdparty.Class", new=Mock):
...
我尝试使用 @pytest.mark.usefixtures("file")
装饰器,但它不起作用。如何将夹具注入 class?
如果您用新的 class Mock
修补了 thirdparty.Class
,那么这意味着在源代码中实例化 thirdparty.Class
的所有调用都将使用 Mock
相反。
解决方案 1
为了能够注入要在 class Mock
中使用的夹具 file
,您必须在 Mock
class 的某处定义它] 可以访问。您无法从 __init__
控制它,因为它将从源代码中调用。您可以做的是将 class Mock
放入函数或夹具中,然后将 file
作为 function/fixture 本身中的变量访问。
thirdparty.py
class MyClass:
def __init__(self, file):
self._file = file
def get(self, *args, **kwargs):
return self._file
def func():
obj = MyClass("/path/to/real")
file = obj.get()
print("File to process:", file)
return file
test_thirdparty.py
from unittest.mock import patch
import pytest
from thirdparty import func
@pytest.fixture(scope="session")
def file():
return "/path/to/mock"
@pytest.fixture
def my_mock_class(file): # This can also be an ordinary function (not a fixture). You just need to pass the <file>.
class MyMockClass:
def __init__(self, *args, **kwargs):
self._file = file # Ignore the entered file in the initialization (__init__). Instead, read the injected file from the current fixture itself (my_mock_class).
def get(self, *args, **kwargs):
return self._file
return MyMockClass
def test_real_file():
assert func() == "/path/to/real"
def test_mock_file(my_mock_class):
with patch("thirdparty.MyClass", new=my_mock_class):
assert func() == "/path/to/mock"
输出
$ pytest -q -rP
.. [100%]
=============================== PASSES ===============================
___________________________ test_real_file ___________________________
------------------------ Captured stdout call ------------------------
File to process: /path/to/real
___________________________ test_mock_file ___________________________
------------------------ Captured stdout call ------------------------
File to process: /path/to/mock
2 passed in 0.05s
解决方案 2
在源代码中,找到实例化它的那些:
the_class = thirdparty.Class(some_file)
然后,跟踪创建 some_file
的位置。假设这是来自函数调用:
some_file = get_file()
然后您需要修补 get_file()
到 return fixture file
的值,以便在创建 thirdparty.Class
时(或者更确切地说 Mock
因为我们已经修补了它),self._file
的值将是夹具中的值。
mocker.patch('get_file", return_value=file) # Where <file> is the fixture
我需要使用一个 fixture 在 class 中准备一些数据,这些数据将用于模拟第三方库。现在我有相当于这个:
@pytest.fixture(scope="session")
def file(tmpdir_factory):
"""Long process that creates a mock file."""
...
return file_path
我需要在 class 构造函数中使用这个固定装置,例如:
class Mock:
def __init__(self, file):
self._file = file
def get(self, *args, **kwargs):
return self._file
我需要 file
夹具在 Mock
class 之外,因为它在其他地方使用。 Mock
class 的用法与此非常相似:
def my_test():
with patch("thirdparty.Class", new=Mock):
...
我尝试使用 @pytest.mark.usefixtures("file")
装饰器,但它不起作用。如何将夹具注入 class?
如果您用新的 class Mock
修补了 thirdparty.Class
,那么这意味着在源代码中实例化 thirdparty.Class
的所有调用都将使用 Mock
相反。
解决方案 1
为了能够注入要在 class Mock
中使用的夹具 file
,您必须在 Mock
class 的某处定义它] 可以访问。您无法从 __init__
控制它,因为它将从源代码中调用。您可以做的是将 class Mock
放入函数或夹具中,然后将 file
作为 function/fixture 本身中的变量访问。
thirdparty.py
class MyClass:
def __init__(self, file):
self._file = file
def get(self, *args, **kwargs):
return self._file
def func():
obj = MyClass("/path/to/real")
file = obj.get()
print("File to process:", file)
return file
test_thirdparty.py
from unittest.mock import patch
import pytest
from thirdparty import func
@pytest.fixture(scope="session")
def file():
return "/path/to/mock"
@pytest.fixture
def my_mock_class(file): # This can also be an ordinary function (not a fixture). You just need to pass the <file>.
class MyMockClass:
def __init__(self, *args, **kwargs):
self._file = file # Ignore the entered file in the initialization (__init__). Instead, read the injected file from the current fixture itself (my_mock_class).
def get(self, *args, **kwargs):
return self._file
return MyMockClass
def test_real_file():
assert func() == "/path/to/real"
def test_mock_file(my_mock_class):
with patch("thirdparty.MyClass", new=my_mock_class):
assert func() == "/path/to/mock"
输出
$ pytest -q -rP
.. [100%]
=============================== PASSES ===============================
___________________________ test_real_file ___________________________
------------------------ Captured stdout call ------------------------
File to process: /path/to/real
___________________________ test_mock_file ___________________________
------------------------ Captured stdout call ------------------------
File to process: /path/to/mock
2 passed in 0.05s
解决方案 2
在源代码中,找到实例化它的那些:
the_class = thirdparty.Class(some_file)
然后,跟踪创建 some_file
的位置。假设这是来自函数调用:
some_file = get_file()
然后您需要修补 get_file()
到 return fixture file
的值,以便在创建 thirdparty.Class
时(或者更确切地说 Mock
因为我们已经修补了它),self._file
的值将是夹具中的值。
mocker.patch('get_file", return_value=file) # Where <file> is the fixture