Python 在模拟中修补发电机

Python 在模拟中修补发电机,python,unit-testing,mocking,Python,Unit Testing,Mocking,我正在尝试为我的单元测试创建生成器方法的模拟版本。设置返回值很容易: patcher = unittest.mock.patch.multiple("__main__.MyClass", method_one=DEFAULT, ... ) patcher.start() MyClass.method_one.return_value = my_mock_gen(some_params) 但是,这只工作一次,每次都必须重置:

我正在尝试为我的单元测试创建生成器方法的模拟版本。设置返回值很容易:

    patcher = unittest.mock.patch.multiple("__main__.MyClass",
        method_one=DEFAULT,
        ...
    )
    patcher.start()
    MyClass.method_one.return_value = my_mock_gen(some_params)
但是,这只工作一次,每次都必须重置:

    for value in my_obj.method_one(some_params):
        # do stuff with value

    my_obj.method_one.reset_mock() # doesn't work with `yield from` as well

另外,我无法基于
某些参数生成模拟值。有没有一种更好的方法来处理我所缺少的这个场景?

解决方案是将
生成器
转换为
列表
进行测试。它起作用了,但可能不好

    #!/usr/bin/python
    import mock
    import unittest


    class FooBar(object):

        def method_one(self, a, b):
            for i in range(a, b):
                yield i * i

        def bar(self):
            return 'bar'


    def mock_generator(a, b):
        for i in range(a, b):
            yield (i + 1) * i


    class TestFooBar(unittest.TestCase):

        def test_method_one(self):
            # set default to a list
            mock_method_one = mock.Mock(return_value=['aaa', 'ccc'])
            # this is how I access the class
            patcher = mock.patch.multiple(
                "python_tools.tests.patch_multiple.FooBar",
                method_one=mock_method_one,
                bar=mock.Mock(return_value='bar')
            )

            patcher.start()
            # method_one return generator only supposed to be read once
            # convert generator to list for testing, crazy?! not sure
            # [6, 12] after converting to list
            mock_method_one.return_value = list(mock_generator(2, 4))
            fb = FooBar()

            # no change
            self.assertEqual(fb.method_one(200, 500), [6, 12])

            # no change
            for item in fb.method_one(2, 5):
                self.assertTrue(item, [6, 12])

            # no change
            self.assertEqual(fb.method_one(1, 6), [6, 12])

            patcher.stop()

如果您想在调用模拟时运行函数,那就是说,不要
返回\u值

MyClass.method_one.side_effect = my_mock_gen

关于重置问题,在
patcher.start()之前,将
method\u one=DEFAULT
,改为
method\u one=mock.mock(return\u value='mocked foo!'),
或定义
return\u value
?问题是应该在运行中生成一个新的迭代器,而不是通过
return\u value=…
重用该迭代器集。我不认为你的建议会改变任何事情,但如果你能提供一个有效的例子,我会接受你的回答。抱歉,但我应该提到,在我的测试中,
method\u one
使用了
yield
(因为它是一个生成器方法)它不会返回一个简单的列表。
副作用
更好,可以更好地控制数据,谢谢!有趣的是,我认为副作用只会产生副作用,而不会影响返回值。