【问题标题】:How to mock the constructor pytest如何模拟构造函数pytest
【发布时间】:2021-02-20 20:48:34
【问题描述】:

类定义如下-

class User:
    def __init__(self):
            self.__client = ManagerClient()
            self.__data = None
    
    def get_data(self):
        if self.__data is None:
            self.__get_password()
        return self.__data

    def __get_password(self):
            self.__password = self.__client.fetch_secret()
        


class ManagerClient:
     def fetch_secret():
         pass

我想对get_data()进行单元测试,因为User类的构造函数正在初始化管理器客户端的对象,并且该对象稍后用于调用fetch_secret(),我应该如何模拟?

【问题讨论】:

  • 我也想为 __get_password 编写单元测试?我应该如何模拟 self.__client.fetch_secret()?

标签: python-3.x object constructor mocking pytest


【解决方案1】:

从技术上讲,在为单元测试测试 get_data 方法时,我们并不关心 __get_password 或其实现。我们只是想测试get_data 的各种逻辑臂。按照这种思路,我们将简单地模拟__get_password 及其操作。如果我们想测试__get_password,我们会为此使用单独的测试。

另一件需要注意的事情是,因为您对属性和方法使用了双下划线,所以它们在文本上被替换为 _classname__attribute。这就是为什么我们使用这种措辞进行修补和模拟,如下所示。你可以看到更多关于here的信息。

另外作为旁注,您的get_data 功能从根本上来说并不健全。根据其逻辑,它有两种不同的返回类型,它可以返回一些东西,也可以在对象上设置一个属性,这被认为是不好的做法。

给定以下项目结构:

stackoverflow/
├── mypackage
│   ├── __init__.py
│   └── users.py
└── tests
    ├── __init__.py
    └── test_users.py

我们要编写的测试如下所示。

from unittest.mock import MagicMock, patch

from mypackage.users import User


@patch("mypackage.users.ManagerClient")
def test_get_data_valid(mock_client):
    expected = "fizz"
    klass = User()

    klass._User__data = expected
    actual = klass.get_data()
    assert actual == expected


@patch("mypackage.users.ManagerClient")
@patch.object(User, "_User__get_password")
def test_get_data_none(mock_pwd, mock_client):
    klass = User()
    klass.get_data()

    mock_pwd.assert_called_once()


@patch("mypackage.users.ManagerClient")
def test_get_password(mock_client):
    mock_client.configure_mock(
        **{
            "fetch_secret.return_value": "buzz",
            "return_value": mock_client
        }
    )

    klass = User()
    klass._User__get_password()

    assert klass._User__password == "buzz"

======================================= test session starts ========================================
platform darwin -- Python 3.9.1, pytest-6.2.2, py-1.10.0, pluggy-0.13.1
rootdir: ***/stackoverflow
collected 3 items                                                                                  

tests/test_users.py ...                                                                      [100%]

======================================== 3 passed in 0.06s =========================================

根据要求,我们在每个测试中模拟 ManagerClient,以便它实际上不会被初始化。这是通过patching相关的类和方法实现的。我们还通过为fetch_secret 设置一个我们可以测试的返回值来测试__get_password 方法。我们让mock_client 在最终测试中返回自身的原因是当您初始化Mock 对象时,它会返回一个新对象。为简单起见,我们只需将 Mock 的返回值设置为自身,这样我们就不必创建新对象了。

【讨论】:

  • 我不想启动ManagerClient的对象,这里的"klass = User()"这行会执行init()并启动ManagerClient。
  • 如果我想测试__get_password怎么办?我应该如何模拟 self.__client.fetch_secret()?
  • 如果这回答了您的问题,请考虑将其标记为已解决
猜你喜欢
  • 2019-11-24
  • 1970-01-01
  • 1970-01-01
  • 2013-09-03
  • 1970-01-01
  • 2015-08-15
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多