xml - 使用 PyTest 进行模拟:改进我的 XML 写入/解析单元测试
问题描述
所以我对 and 比较pytest
陌生mock
,但仍然有经验junit
和嘲笑mockito
for groovy
(它带有一个方便的when(...).thenAnswer/Return
功能)
我写了一个简单的类来解析和编写 xml 文件。这个类存在的唯一目的是为了对我目前正在开发的插件进行单元测试。这个个人项目也被用作一个学习工具来帮助我完成我的工作职责(基于 devOps python)
显然,我也需要测试它。
这是课程:
from lxml import etree
from organizer.tools.exception_tools import ExceptionPrinter
class XmlFilesOperations(object):
@staticmethod
def write(document_to_write, target):
document_to_write.write(target, pretty_print=True)
@staticmethod
def parse(file_to_parse):
parser = etree.XMLParser(remove_blank_text=True)
try:
return etree.parse(file_to_parse, parser)
except Exception as something_happened:
ExceptionPrinter.print_exception(something_happened)
这是它的单元测试:
import mock
from organizer.tools.xml_files_operations import XmlFilesOperations
FILE_NAME = "toto.xml"
@mock.patch('organizer.tools.xml_files_operations.etree.ElementTree')
def test_write(mock_document):
XmlFilesOperations.write(mock_document, FILE_NAME)
mock_document.write.assert_called_with(FILE_NAME, pretty_print=True)
@mock.patch('organizer.tools.xml_files_operations.etree')
def test_parse(mock_xml):
XmlFilesOperations.parse(FILE_NAME)
mock_xml.parse.assert_called()
此外,这里是用于此 python 环境的 pipfile:
[[source]]
url = "https://pypi.python.org/simple"
verify_ssl = true
name = "pypi"
[packages]
lxml = "*"
pytest = "*"
pytest-lazy-fixture = "*"
mock = "*"
MKLpy = "*"
我想通过使用assert_called_with
函数中的test_parse
函数来改进这个测试。然而,为了让它工作,我需要得到该XmlFilesOperations.parse
方法中使用的确切解析器,所以我也想模拟它。为此,我需要etree.XMLParser(remove_blank_text=True)
调用返回一个模拟对象
这是我尝试过的:
import mock
import pytest
from lxml import etree
from organizer.tools.xml_files_operations import XmlFilesOperations
FILE_NAME = "toto.xml"
@pytest.fixture()
def mock_parser():
parser = mock.patch('organizer.tools.xml_files_operations.etree.XMLParser').start()
with mock.patch('organizer.tools.xml_files_operations.etree.XMLParser', return_value=parser):
yield parser
parser.stop()
@mock.patch('organizer.tools.xml_files_operations.etree')
def test_parse(mock_xml, mock_parser):
XmlFilesOperations.parse(FILE_NAME)
mock_xml.parse.assert_called_with(FILE_NAME, mock_parser)
我收到以下错误:
def raise_from(value, from_value):
> raise value
E AssertionError: expected call not found.
E Expected: parse('toto.xml', <MagicMock name='XMLParser' id='65803280'>)
E Actual: parse('toto.xml', <MagicMock name='etree.XMLParser()' id='66022384'>)
所以调用返回的模拟对象与我创建的模拟对象不同。
使用 Mockito,我会做这样的事情:
parser = etree.XmlParser()
when(etree.XMLParser(any()).thenReturn(parser)
它会起作用。我该如何解决?
解决方案
您的方法的主要问题是模拟对象的顺序。夹具首先被调用,在模拟解析器时,它不使用 mocked etree
,而是真正的,而在测试中,解析器是从 mocked 使用的etree
,这是由该模拟创建的另一个模拟。
此外,您确实检查了解析器方法而不是解析器本身。
以下是不使用固定装置的情况:
@mock.patch('organizer.tools.xml_files_operations.etree.XMLParser')
@mock.patch('organizer.tools.xml_files_operations.etree')
def test_parse(mock_xml, mock_parser):
XmlFilesOperations.parse(FILE_NAME)
mock_xml.parse.assert_called_with(FILE_NAME, mock_parser())
另一种可能性是交换夹具和补丁,以便以正确的顺序使用它们:
@pytest.fixture()
def mock_etree():
with mock.patch('organizer.tools.xml_files_operations.etree') as mocked_etree:
yield mocked_etree
@mock.patch('organizer.tools.xml_files_operations.etree.XMLParser')
def test_parse(mock_xml_parser, mock_etree):
XmlFilesOperations.parse(FILE_NAME)
mock_etree.parse.assert_called_with(FILE_NAME, mock_xml_parser())
最后,如果你只想使用fixtures,你可以让它们相互依赖:
@pytest.fixture()
def mock_etree():
with mock.patch('organizer.tools.xml_files_operations.etree') as mocked_etree:
yield mocked_etree
@pytest.fixture()
def mock_parser(mock_etree):
parser = mock.Mock()
with mock.patch.object(mock_etree, 'XMLParser', parser):
yield parser
def test_parse(mock_parser, mock_etree):
XmlFilesOperations.parse(FILE_NAME)
mock_etree.parse.assert_called_with(FILE_NAME, mock_parser())
推荐阅读
- string - 将字符串转换为标题大小写 - Emacs Lisp
- java - 服务器发送请求 firebase java.net.protocolexception 在读取响应后无法写入请求正文
- apache-kafka - LoggingMessageFormatter 与 kafka-avro-console-consumer
- javascript - 通过Angular 2获取本机元素相对于视口的位置
- curl - Q ZAPIER 卷曲请求
- angular - 在固定目标的情况下跳过 polyfill 的角度提示 (Chrome 67)
- javascript - 如何从 Steam 中的其他个人资料中获取愿望清单数据?
- react-native - 保存多个图像时,CameraRoll.saveToCameraRoll“写忙”错误
- javascript - 在 index.js 之前运行可执行文件
- javascript - 遍历 .replace() 中的数组