使用patch decorator和side_effect模拟文件读取

时间:2017-08-05 02:04:16

标签: python unit-testing testing mocking

我正在测试一个读取文件的函数,对内容进行操作并根据它看到的内容返回一个值。我测试的函数testme位于module.py。我正在运行python 2.7。我知道我可以用

完成这个任务
import unittest
import module
from mock import patch, mock_open

TestTestMe(unittest.TestCase):
    ...
    def test_test_me(self):
        with patch('module.open', mock_open(read_data='1 2')) as _:
          self.assertRaises(IndexError, module.testme, 'foo')
        with patch('module.open', mock_open(read_data='1 2 3')) as _:
          self.assertEquals(module.testme('foo'), 3)

但是,我想(主要是为了防止重复使用with语句,并且还能够动态生成各种read_data)能够使用@patch作为装饰器来定义带有函数的read_data 。像这样的东西。我不会重复类定义和导入。

def give_contents(x):
    if x == 'correct':
        return mock_open(read_data='1 2 3')
    else:
        return mock_open(read_data='a b c')

然后使用测试函数,如:

@patch(module.open, side_effect=give_contents)
def test_test_me(self):
    self.assertEquals(module.testme('correct'), 3)

我一直在运行TypeErrors,例如

TypeError: test_testme() takes exactly 1 argument (2 given)
然而,我试图解决这个问题。这真让我抓狂。指导将不胜感激。如果你想要一些我可能已经省略的细节,请询问具体细节,我会提供这些细节。

编辑:按要求执行要测试的功能。对不起,我把它省略为“不重要”,显然应该在那里。

def testme(filepath):
    with open(filepath, 'r') as f:
        line = f.readline().strip().split()
    return int(line[2])

2 个答案:

答案 0 :(得分:1)

正如我在之前的评论中所述: 我不确定,因为你没有在你给我们的代码中包含任何test_testme函数。但是,如果它是您正在使用的module.testme方法,则忘记在方法定义中声明字符串参数。根据您的反馈,我可能会给出答案。

编辑:我当时并不完全正确,因为你忘记的论点是自我。

显然,这对你有用,所以这是承诺的答案。

假设您谈到的module.testme方法是一个类似于以下的函数:

TestTestMe(unittest.TestCase):
...
def testme(filepath):
with open(filepath, 'r') as f:
    line = f.readline().strip().split()
return int(line[2])

然而,当您从对象访问它时,此函数是一种方法。 (执行module.testme('foo')),因此,给调用的第一个参数将始终是隐式self

所以会发生什么,你的函数期望一个参数,一个字符串(' foo'),但即使自己不是明确的,也会给出两个:'(自我, '富')&#39 ;.

这样的错误就是说你收到的论据比你要求的多。 更正非常简单:将self添加到testme的预期参数中。

然后会变成:

def testme(self, filepath):
    with open(filepath, 'r') as f:
        line = f.readline().strip().split()
    return int(line[2])

希望这有帮助。关于论证数量的错误实际上通常是由于这种被遗忘的细节。虽然你不需要自我,但它总是作为第一个位置参数传递(在python中)。

祝你有愉快的一天! PS:对不起有些奇怪的英语短语和重复。

答案 1 :(得分:0)

我会考虑以下事项:

from io import TextIOWrapper, BytesIO
from unittest.mock import patch, mock_open

def open_file(filename):
    with open(filename, 'r') as f:
        data = f.readline()
    return data

def string_byte_me(input):
    return TextIOWrapper(BytesIO(input))

def side_effect(filename, mode):
    if filename == 'correct':
        return string_byte_me(b'1 2 3')
    else:
        return string_byte_me(b'a b c')

m = mock_open()
m.side_effect = side_effect
@patch('builtins.open', m)
def test_open_file():
    assert open_file('correct') == '1 2 3'
    assert open_file('incorrect') == 'a b c'
test_open_file() # Passes.

这可以通过在实例化之后将side_effect添加到mock_open对象来实现(不确定是否有更好的方法?)。返回的side_effect必须能够.readline()因此是TextIOWrapper。