So I am rather new with pytest and mock, but still have experience with junit and mocking with mockito for groovy (which comes with an handy when(...).thenAnswer/Return function)
I wrote a simple class to parse and write xml files. This class sole purpose for existence is to be mocked in order to unit test the plugin I am currently working on. This personal project is also used as a learning tool to help me in my work duties (devOps python based)
Obviously, I needed to test it too.
Here is the class:
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) And here is the unit test for it:
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() Also, here is the pipfile used for this python environment:
[[source]] url = "https://pypi.python.org/simple" verify_ssl = true name = "pypi" [packages] lxml = "*" pytest = "*" pytest-lazy-fixture = "*" mock = "*" MKLpy = "*" I would like to improve this test by making use of the assert_called_with function in the test_parse function. However to make it work I need to get the exact parser that is used in the XmlFilesOperations.parse method so I imagined mocking it too. For this I need the etree.XMLParser(remove_blank_text=True) call to return a mocked object
Here is what I tried:
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) I obtain the following error:
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'>) So the mocked object returned by the call is not the same mocked object that I created.
With Mockito, I would have done something like this:
parser = etree.XmlParser() when(etree.XMLParser(any()).thenReturn(parser) And it would work. How could I fix that ?