我有以下配置类:
class ConfigB(object):
Id = None
def __Init__(self, Id):
self.Id = Id
在以下类中实例化并打印属性:
from config.ConfigB import ConfigB
class FileRunner(object):
def runProcess(self, Id)
cfgB = ConfigB(Id)
print(cfgB.Id)
我创建了以下测试类来测试它,我试图模拟实例化和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)
要正确获取配置属性,我需要做什么?
另请注意我的配置&#39;类实例化比上面显示的要复杂得多,这就是为什么我要修补实例化和调用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)
我现在从两个打印语句中获得以下输出:
<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
答案 0 :(得分:1)
在我看来,只需替换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')
将使用模拟替换ConfigB
命名空间中的FileRunner
类。然后,您可以配置模拟以执行您喜欢的任何操作 - 例如有Id
等于17。
答案 1 :(得分:0)
要模拟Id属性,您可以使用Mock实例化的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
答案 2 :(得分:0)
最近,我遇到了一个稍微复杂的问题,通过为return_value
中使用的模拟设置mock.patch
来解决类似的问题(如下所示)。
您可以进一步重构您的解决方案,以将mock.patch
用作context manager(链接部分中的Ctrl-F“带有修补程序”)代替装饰器,从而对使用的对象进行更多控制:
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)
我想在某些类中测试条件日志记录(简化的代码示例)。经过测试的模块:
# tested_module.py
import logging
from exceptions import CustomExceptionClass
class ClassUnderTest:
logger = logging.getLogger(__name__)
def tested_instance_method(self, arguments):
... # Some more code.
if 2 in arguments:
self.logger.exception(CustomExceptionClass(arguments))
... # Some more code.
正在测试(解决问题后):
# 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
是正确的答案。