首页
学习
活动
专区
圈层
工具
发布
社区首页 >问答首页 >如何使用pytest-mock来模拟python类依赖项?

如何使用pytest-mock来模拟python类依赖项?
EN

Stack Overflow用户
提问于 2021-09-07 10:09:02
回答 1查看 2.3K关注 0票数 0

我很难理解如何在Python & pytest中对类依赖项中的所有方法进行存根/模拟。下面的清单显示了我正在测试的类。它有两个内部依赖关系:OWMProxyPyOwmDeserializer

测试下的

代码语言:javascript
复制
class OWM:
    def __init__(self, api_key: str, units: WeatherUnits) -> None:
        self._api_key = api_key
        self._units = units    
        self._pyorm = OWMProxy.from_api_key(api_key)
        self._deserializer = PyOwmDeserializer()

    def at(self, city: str, iso_datetime: str) -> ForecastModel:
        weather = self._pyorm.for_time(city, iso_datetime)

        return self._deserializer.deserialize(weather, self._units)

    def day(self, city: str, day: str) -> ForecastModel:
        weather = self._pyorm.for_day(city, day)

        return self._deserializer.deserialize(weather, self._units)

    def now(self, city: str) -> ForecastModel:
        weather = self._pyorm.now(city) 

        return self._deserializer.deserialize(weather, self._units)

我的问题是,当使用PyTest进行单元测试时,是否有可能模拟整个类依赖项?

目前,我所使用的单元测试可以模拟每个类方法,包括init方法。

我可以使用依赖注入方法,即为内部反序列化器和代理接口创建一个接口,并将这些接口添加到被测试类的构造函数中。

或者,我可以使用unittest.mock模块进行测试,就像建议的这里一样。pytest-mock中是否有类似的功能?

到目前为止单元测试.

代码语言:javascript
复制
@pytest.mark.skip(reason="not implemented")
  def test_owm_initialises_deserializer(
      default_weather_units: WeatherUnits, mocker: MockFixture
  ) -> None:
      api_key = "test_api_key"
  
      proxy = OWMProxy(py_OWM(api_key))
  
      patch_proxy = mocker.patch(
          "wayhome_weather_api.openweathermap.client.OWMProxy.from_api_key",
          return_value=proxy,
      )   
  
      patch_val = mocker.patch(
          "wayhome_weather_api.openweathermap.deserializers.PyOwmDeserializer",
          "__init__",
          return_value=None,
      )   
  
      owm = OWM(api_key, default_weather_units)
  
      assert owm is not None
EN

回答 1

Stack Overflow用户

回答已采纳

发布于 2021-09-07 10:56:00

您只需模拟整个类并控制其方法的返回值和/或副作用,就像在文档中所做的那样。

def some_function():.实例= module.Foo()。返回instance.method() . >>>和补丁(‘module.Foo’)作为模拟:.实例= mock.return_value ..。instance.method.return_value =“结果”。结果= some_function() .断言结果==“结果”

假设所测试的类位于src.py中。

test_owm.py

代码语言:javascript
复制
import pytest
from pytest_mock.plugin import MockerFixture

from src import OWM, WeatherUnits


@pytest.fixture
def default_weather_units():
    return 40


def test_owm_mock(
      default_weather_units: WeatherUnits, mocker: MockerFixture
) -> None:
    api_key = "test_api_key"

    #  Note that you have to mock the version of the class that is defined/imported in the target source code to run. So here, if the OWM class is located in src.py, then mock its definition/import of src.OWMProxy and src.PyOwmDeserializer
    patch_proxy = mocker.patch("src.OWMProxy.from_api_key")
    patch_val = mocker.patch("src.PyOwmDeserializer")

    owm = OWM(api_key, default_weather_units)

    assert owm is not None

    # Default patch
    print("Default patch:", owm.day("Manila", "Today"))

    # Customizing the return value
    patch_proxy.return_value.for_day.return_value = "Sunny"
    patch_val.return_value.deserialize.return_value = "Really Sunny"
    print("Custom return value:", owm.day("Manila", "Today"))
    patch_proxy.return_value.for_day.assert_called_with("Manila", "Today")
    patch_val.return_value.deserialize.assert_called_with("Sunny", default_weather_units)

    # Customizing the side effect
    patch_proxy.return_value.for_day.side_effect = lambda city, day: f"{day} in hot {city}"
    patch_val.return_value.deserialize.side_effect = lambda weather, units: f"{weather} is {units} deg celsius"
    print("Custom side effect:", owm.day("Manila", "Today"))
    patch_proxy.return_value.for_day.assert_called_with("Manila", "Today")
    patch_val.return_value.deserialize.assert_called_with("Today in hot Manila", default_weather_units)


def test_owm_stub(
      default_weather_units: WeatherUnits, mocker: MockerFixture
) -> None:
    api_key = "test_api_key"

    class OWMProxyStub:
        @staticmethod
        def from_api_key(api_key):
            return OWMProxyStub()

        def for_day(self, city, day):
            return f"{day} in hot {city}"

    class PyOwmDeserializerStub:
        def deserialize(self, weather, units):
            return f"{weather} is {units} deg celsius"


    patch_proxy = mocker.patch("src.OWMProxy", OWMProxyStub)
    patch_val = mocker.patch("src.PyOwmDeserializer", PyOwmDeserializerStub)

    owm = OWM(api_key, default_weather_units)

    assert owm is not None

    # Default patch
    print("Default patch:", owm.day("Manila", "Today"))
    # If you want to assert the calls made as did in the first test above, you can use the mocker.spy() functionality

输出

代码语言:javascript
复制
$ pytest -q -rP
================================================================================================= PASSES ==================================================================================================
______________________________________________________________________________________________ test_owm_mock ______________________________________________________________________________________________
------------------------------------------------------------------------------------------ Captured stdout call -------------------------------------------------------------------------------------------
Default patch: <MagicMock name='PyOwmDeserializer().deserialize()' id='139838844832256'>
Custom return value: Really Sunny
Custom side effect: Today in hot Manila is 40 deg celsius
______________________________________________________________________________________________ test_owm_stub ______________________________________________________________________________________________
------------------------------------------------------------------------------------------ Captured stdout call -------------------------------------------------------------------------------------------
Default patch: Today in hot Manila is 40 deg celsius
2 passed in 0.06s

如您所见,我们能够控制模拟依赖项的方法的返回值。

票数 2
EN
页面原文内容由Stack Overflow提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://stackoverflow.com/questions/69086247

复制
相关文章

相似问题

领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档