在类方法中模拟/修补特定的对象方法



我知道标题很混乱,但为了清楚起见,我创建了一个我必须转换的示例:

假设在class_file.py

中有这段代码
class CarDealership:
def allow_car_out_of_the_dealership(self, car):
logger.info(f"Driving out of the dealership")

drivers_license = car.driver.wallet.get_drivers_license()
if drivers_license:
try:
allow_car_removal(car, drivers_license)
except:
deny_car_removal(car, drivers_license)

我正试图测试方法deny_car_removal被调用的情况。get_valid_license()driver.wallet特有的方法,在class_file.py中不以任何方式、形式或形式导入或引用。

问题是,如果get_valid_license在实际情况下没有返回任何有效的内容,那么代码将完全不同。我正试图修补drivers_license,所以我最终可以把它带到我需要的点。

我猜test_class_file.py应该看起来像这样:

class CarDealershipTests(BaseTestCase):
@patch('class_file.CarDealership.deny_car_removal')
def test_deny_car_removal(self):
# something here
self.assertTrue(mock_deny_car_removal.called)

我从其他StackOverflow答案中尝试了一些事情,但没有工作:

  • 在测试设置中,在被模拟的car中模拟一个完整的driver实例,如下所示:
def setUp(self) -> None:
self._create_car

def _create_car(self):
car = Car()
car.driver = MagicMock()
  • 尝试使用@patch.object(class_file.CarDealership.allow_car_out_of_the_dealership, "car.driver.wallet.get_valid_license")
  • 尝试使用@patch.object(class_file.CarDealership.allow_car_out_of_the_dealership, "drivers_license")
  • 尝试使用@patch("class_file.CarDealership.allow_car_out_of_the_dealership.car.driver.wallet.get_valid_license")
  • 尝试使用@patch("class_file.CarDealership.allow_car_out_of_the_dealership.drivers_license")

假设您有一个包含如下内容的文件:

import logging
logger = logging.getLogger(__name__)
class CarRemovalFailure(BaseException):
pass

class CarDealership:
def allow_car_out_of_the_dealership(self, car):
logger.info("Driving out of the dealership")

drivers_license = car.driver.wallet.get_drivers_license()
if drivers_license:
try:
allow_car_removal(car, drivers_license)
except CarRemovalFailure:
deny_car_removal(car, drivers_license)

为了调用deny_car_removal,我们需要allow_car_removal抛出一个Exception。这很容易通过使用Mock对象的side_effect属性来完成。

我没有在这个测试中加入任何设置/拆除,而是说明side_effect是如何工作的,以及deny_car_removal是如何被调用的。

from unittest.mock import MagicMock, patch
from class_file import CarDealership, CarRemovalFailure

@patch("class_file.allow_car_removal")
@patch("class_file.deny_car_removal")
def test_deny_car(mock_deny, mock_allow):
mock_allow.side_effect = CarRemovalFailure("Failed!")
car = MagicMock()
cd = CarDealership()
cd.allow_car_out_of_the_dealership(car)
car.driver.wallet.get_drivers_license.assert_called_once()
mock_deny.assert_called_once()

运行时输出如下:

============================= test session starts ==============================
platform darwin -- Python 3.9.1, pytest-6.2.2, py-1.10.0, pluggy-0.13.1
rootdir: ****
collected 1 item                                                               
tests/test_car.py .                                                      [100%]
============================== 1 passed in 0.05s ===============================

在你的例子中,你可能不想修补deny_car_removal,因为你试图测试逻辑,但因为我不知道那可能是什么,我在我的端打了补丁。这样做的目的是为了说明实际上函数确实被调用了。