简体   繁体   English

如何解决调用中的嘲讽_开放差异,但不解决最终结果

[英]How to fix mock_open differences in calls but not in end result

Using mock_open , I can capture the data from writes using the with [...] as construct. 通过使用mock_open ,我可以使用with [...] as构造来捕获写入数据。 However, testing that what I have is correct is a little tricky. 但是,测试我所拥有的是否正确有些棘手。 For example, I can do this: 例如,我可以这样做:

>>> from mock import mock_open
>>> m = mock_open()
>>> with patch('__main__.open', m, create=True):
...     with open('foo', 'w') as h:
...         h.write('some stuff')
...
>>> m.mock_calls
[call('foo', 'w'),
 call().__enter__(),
 call().write('some stuff'),
 call().__exit__(None, None, None)]
>>> m.assert_called_once_with('foo', 'w')
>>> handle = m()
>>> handle.write.assert_called_once_with('some stuff')

But I want to do compare what I think should have been written to what was. 但我想将我认为应该写的内容与以前写的内容进行比较。 In effect something like this: 实际上是这样的:

>>> expected = 'some stuff'
>>> assert(expected == m.all_that_was_written)

The problem I am facing with call is that different versions of json (2.0.9 vs 1.9) seem to print things differently. 我在call面临的问题是,不同版本的json(2.0.9与1.9)似乎在打印内容上有所不同。 No, I cannot just update to the latest json. 不,我不能只是更新到最新的json。

The actual error I am getting is this: 我得到的实际错误是这样的:

E           AssertionError: [call('Tool_000.json', 'w'),
                             call().__enter__(),
                             call().write('['),
                             call().write('\n  '),
                             call().write('"1.0.0"'),
                             call().write(', \n  '),
                             call().write('"2014-02-27 08:58:02"'),
                             call().write(', \n  '),
                             call().write('"ook"'),
                             call().write('\n'),
                             call().write(']'),
                             call().__exit__(None, None, None)] 
            !=
                            [call('Tool_000.json', 'w'),
                             call().__enter__(),
                             call().write('[\n  "1.0.0"'),
                             call().write(', \n  "2014-02-27 08:58:02"'),
                             call().write(', \n  "ook"'),
                             call().write('\n'),
                             call().write(']'),
                             call().__exit__(None, None, None)]

In effects, the calls are different but the end result is the same. 实际上,调用是不同的,但最终结果是相同的。

The code I am testing is fairly simple: 我正在测试的代码非常简单:

with open(get_new_file_name(), 'w') as fp:
    json.dump(lst, fp)

So, creating another method that passes the file pointer seems overkill. 因此,创建另一个传递文件指针的方法似乎有些过分。

You can patch open() to return StringIO object and then check the contents. 您可以修补open()以返回StringIO对象,然后检查其内容。

with mock.patch('module_under_test.open', create=True) as mock_open:
    stream = io.StringIO()
    # patching to make getvalue() work after close() or __exit__()
    stream.close = mock.Mock(return_value=None)
    mock_open.return_value = stream

    module_under_test.do_something() # this calls open()

    contents = stream.getvalue()
    assert(contents == expected)

Edit : added patch for stream.close to avoid exception on stream.getvalue() . 编辑 :为stream.close添加了补丁,以避免stream.getvalue()异常。

mock_open is not fully featured yet. mock_open尚未完全提供。 It works well if you are mocking files to be read but it does not yet have enough features for testing written files. 如果要嘲笑要读取的文件,它会很好地工作,但它尚不足以测试书面文件。 The question clearly shows this deficiency. 问题清楚地表明了这种缺陷。

My solution is to not use mock_open if you are testing the written content. 我的解决方案是,如果您正在测试书面内容,请不要使用mock_open打开。 Here is the alternative: 这是替代方法:

import six
import mock
import unittest

class GenTest(unittest.TestCase):
    def test_open_mock(self):
        io = six.BytesIO()
        io_mock = mock.MagicMock(wraps=io)
        io_mock.__enter__.return_value = io_mock
        io_mock.close = mock.Mock() # optional
        with mock.patch.object(six.moves.builtins, 'open', create=True, return_value=io_mock):
            # test using with
            with open('foo', 'w') as h:
                expected = 'some stuff'
                h.write(expected)
            self.assertEquals(expected, io.getvalue())
            # test using file handle directly
            io.seek(0); io.truncate() # reset io
            expected = 'other stuff'
            open('bar', 'w').write(expected)
            self.assertEquals(expected, io.getvalue())
            # test getvalue after close
            io.seek(0); io.truncate() # reset io
            expected = 'closing stuff'
            f = open('baz', 'w')
            f.write(expected)
            f.close()
            self.assertEquals(expected, io.getvalue())

if __name__ == '__main__':
    unittest.main()

Here's what I will do, write a method that returns the complete string from all calls of the write method. 这是我要做的,编写一个方法,该方法从write方法的所有调用中返回完整的字符串。

class FileIOTestCase(unittest.TestCase):
    """ For testing code involving file io operations """
    def setUp(self):
        """ patches the open function with a mock, to be undone after test. """
        self.mo = mock_open()

        patcher = patch("builtins.open", self.mo)
        patcher.start()
        self.addCleanup(patcher.stop)

    def get_written_string(self):
        return ''.join(c[0][0] for c in self.mo.return_value.write.call_args_list)

An example of how to use it 如何使用它的一个例子

class TestWriteFile(FileIOTestCase):
    def test_write_file__csv(self):
        save.write_file("a,b\n1,2", "directory", "C6L")
        self.mo.assert_called_once_with(os.path.join("directory", "C6L.csv"), 'w')
        self.assertEqual(self.get_written_string(), "a,b\n1,2")

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM