模拟返回MagicMock而不是valu的属性调用

2024-10-01 07:26:51 发布

您现在位置:Python中文网/ 问答频道 /正文

我有以下配置类:

class ConfigB(object):
  Id = None

  def __Init__(self, Id):
    self.Id = Id

在以下类中实例化并打印属性:

^{pr2}$

我创建了下面的测试类来测试它,在这里我试图模拟实例化和cfgB.Id.代码物业电话:

import unittest
import unittest.mock imort MagicMock
import mock
from FileRunner import FileRunner

class TestFileRunner(unittest.TestCase):
  @mock.patch('ConfigB.ConfigB.__init__')
  @mock.patch('ConfigB.ConfigB.Id')
  def test_methodscalled(self, cfgBId, cfgBInit):


    fileRunner = FileRunner()

    cfgBId.return_value = 17

    cfgBInit.return_value = None

    print(cfgBId)

    fileRunner.runProcess(1)

在调用fileRunner之前,请注意print(cfgBId)语句。我得到以下输出:

<MagicMock name='Id' id='157297352'>
<MagicMock name='Id' id='157297352'>

由于某些原因,当我在测试类中设置返回值时:

cfgBId.return_value = 17

在FileRunner()类中的行上没有调用:

print(cfgB.Id)

要正确显示配置属性,需要做些什么?在

还要注意,我的'ConfigB'类实例化比上面显示的要复杂得多,这就是为什么我要修补实例化和对Id属性的调用。在

*更新:我已经按照@mgilson的建议更改了我的类,但仍然不起作用:

import unittest
import unittest.mock imort MagicMock
import mock
from FileRunner import FileRunner

class TestFileRunner(unittest.TestCase):
  @mock.patch('FileRunner.ConfigB')
  def test_methodscalled(self, cfgB):

    fileRunner = FileRunner()

    cfgB.Id = 17

    print(cfgBId)

    fileRunner.runProcess(1)

我现在从这两个print语句得到以下输出:

<MagicMock name='ConfigB' id='157793640'>
<MagicMock name='ConfigB().Id' id='157020512'>

你知道为什么上面的方法不起作用吗?在

*解决方案:

在对@mgilson建议的测试方法稍作修改后,我得以使其发挥作用:

import unittest
from unittest.mock import MagicMock
import mock
from FileRunner import FileRunner

class TestFileRunner(unittest.TestCase):
  @mock.patch('FileRunner.ConfigB')
  def test_methodscalled(self, configB):

    fileRunner = FileRunner()

    cfgB = MagicMock()
    cfgB.Id = 17
    #This will return the cfgB MagicMock when `ConfigB(Id)` is called in `FileRunner` class
    configB.return_value = cfgB 

    print(cfgB.Id)

    fileRunner.runProcess(1)

    #To test whether `ConfigB(17)` was called
    configB.assert_called_with(17)

我现在得到以下输出:

<MagicMock name='ConfigB' id='157147936'>
17

Tags: nameimportselfidreturnunittestmockclass
3条回答

要使用mock Id属性,您可以使用实例化了该属性的mock来修补类,如下所示:

@mock.patch('ConfigB.ConfigB', Mock(Id='17'))
def test_methodscalled(self, cfgB):
    cfgB.return_value.__init__.return_value = None # FWIW, this isn't necessary

在我看来,替换FileRunner命名空间中的整个ConfigB对象会更好。那么你的测试看起来像这样:

import unittest
import unittest.mock imort MagicMock
import mock
from FileRunner import FileRunner

class TestFileRunner(unittest.TestCase):
  @mock.patch('FileRunner.ConfigB')
  def test_methodscalled(self, cfgB):
    fileRunner = FileRunner()
    cfgB.return_value.Id = 17
    fileRunner.runProcess(1)

注意,@mock.patch('FileRunner.ConfigB')将用mock替换FileRunner命名空间中的ConfigB类。然后,您可以配置mock以执行任何您喜欢的操作,例如,Id等于17。在

最近,我遇到了一个稍微复杂的问题,我用类似的方法解决了这个问题:为mock.patch中使用的mock设置return_value(如下所示)。在

通过使用mock.patch作为context manager(linked section中的Ctrl-F“with patch”代替decorator),可以进一步重构您的解决方案,使您能够更好地控制使用的对象:

class TestFileRunner(unittest.TestCase):
  def test_methodscalled(self):
    id = 17

    with mock.patch('FileRunner.ConfigB', return_value=Mock(Id=id)) as configB:
        FileRunner().runProcess(1)

    configB.assert_called_with(id)

我的例子:断言调用是通过使用修补的对象实例进行的

我想在一些类中测试条件日志(代码示例简化)。测试模块:

^{pr2}$

工作测试(解决问题后):

# test_module.py
from unittest import mock
import tested_module

class TestModule:
    def test_exception_logged(self):
        method_arguments = 1, 2, 3
        logger_mock = mock.Mock()
        tested_class_instance = mock.Mock(logger=logger_mock)
        exception_mock_instance = mock.Mock()

        with mock.patch.object(tested_module, 'CustomExceptionClass',
                               return_value=exception_mock_instance) as exception_mock:
            tested_module.ClassUnderTest.tested_instance_method(tested_class_instance,
                                                                method_arguments)

            logger_mock.exception.assert_called_once()
            self.assertSequenceEqual(logger_mock.exception.call_args_list,
                                     [mock.call(exception_mock_instance)])
            exception_mock.assert_called_once()
            self.assertSequenceEqual(exception_mock.call_args_list,
                                     [mock.call(method_arguments)])

在我提出上述解决方案之前,有问题的部分是:

# Check below fails.
self.assertSequenceEqual(logger_mock.exception.call_args_list,
                         [mock.call(exception_mock)])  # Wrong object, "()" problem.

然后我试了一下:

self.assertSequenceEqual(logger_mock.exception.call_args_list,
                         [mock.call(exception_mock())])  # Can't do `exception_mock()` ...
# ... because it was called 2nd time and assertion below will fail.
exception_mock.assert_called_once()

问题是,我必须以某种方式获取另一个模拟对象,当调用exception_mock时返回该对象,并且没有第二次调用exception_mock。因此,预先创建mock.Mock实例并分配给return_value是正确的答案。在

相关问题 更多 >