【问题标题】:Use pytest fixture inside mock class在模拟类中使用 pytest 夹具
【发布时间】:2021-11-11 15:50:53
【问题描述】:

我需要使用一个夹具来准备一个类中的一些数据,这些数据将用于模拟第三方库。现在我有这个等价物:

@pytest.fixture(scope="session")
def file(tmpdir_factory):
    """Long process that creates a mock file."""
    ...
    return file_path
    

我需要在类构造函数中使用这个fixture,比如:

class Mock:

    def __init__(self, file):
        self._file = file

    def get(self, *args, **kwargs):
        return self._file

我需要file 固定装置在Mock 类之外,因为它在其他地方使用。 Mock 类的使用与此非常相似:


def my_test():
    with patch("thirdparty.Class", new=Mock):
        ...

我尝试使用 @pytest.mark.usefixtures("file") 装饰器,但它不起作用。如何将夹具注入类?

【问题讨论】:

    标签: python python-3.x testing pytest python-unittest


    【解决方案1】:

    如果您使用新类 Mock 修补了 thirdparty.Class,那么这意味着在源代码中实例化 thirdparty.Class 的所有调用都将使用 Mock

    解决方案 1

    为了能够注入夹具file 以在Mock 类中使用,您必须在Mock 类可以访问的某个位置定义它。您无法从__init__ 控制它,因为它将从源代码中调用。您可以做的是将该类 Mock 放入函数或夹具中,然后将 file 作为函数/夹具本身中的变量访问。

    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() 如何返回夹具 file 的值,以便在创建 thirdparty.Class(或者更确切地说是 Mock,因为我们已经修补它)时,self._file 的值将是夹具中的那个。

    mocker.patch('get_file", return_value=file)  # Where <file> is the fixture
    

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2019-07-13
      • 1970-01-01
      • 1970-01-01
      • 2020-12-20
      • 1970-01-01
      相关资源
      最近更新 更多