【问题标题】:access python unittest magicmock return value访问python unittest magicmock返回值
【发布时间】:2021-04-07 15:10:05
【问题描述】:

我正在使用带有 unittest 和 mock 的 python 3.9.2 来修补一个类。

我的测试代码实例化了该类的一个对象,mock 返回一个 MagicMock 对象作为实例。

我的问题是,我可以从我的测试代码中访问该对象吗?

我可以在 mock_calls 列表中看到实例化类的调用,但找不到访问从该调用返回的实例的方法。

我需要访问实例的原因是我的测试代码将属性附加到实例而不是调用实例上的方法。测试方法调用很容易,但是有没有直接测试属性的方法?

【问题讨论】:

  • 添加问题的最小可行示例,以帮助人们更好地理解场景。

标签: python unit-testing mocking


【解决方案1】:

经过调查,我发现每次实例化我的类时,只有一个 MagicMock 实例被创建并返回。由于我添加到类中的属性,这种行为对我来说并不方便。

我创建了以下测试辅助工具来支持我的需求。这不是通用的,但可以适应其他情况。

class MockMyClass():
"""mock multiple MyClass instances
Note - the code under test must add a name attribute to each instance
"""

def __init__(self):
    self.myclass = []

def factory(self, /, *args, **kwargs):
    """return a new instance each time called"""
    new = mock.MagicMock()
    # override __enter__ to enable the with... context manager behaviour
    # for convenience in testing
    new.__enter__ = lambda x: new
    self.myclass.append(new)
    return new

def __getitem__(self, key: str) -> None:
    """emulate a dict by returning the named instance
    use as
    mockmyclass['name'].assert_called_once()
    or
    with mockmyclass['name'] as inst:
        inst.start().assert_called_once()
    """
    # Important - the code under test gives the instance a name
    # attribute and this relies on that attribute so is not
    # general purpose
    wanted = [t for t in self.myclass if t.name == key]
    if not wanted:
        names = [t.name for t in self.myclass]
        raise ValueError(f'no timer {key} in {names}')
    return wanted[0]

类TestBehaviour(unittest.TestCase):

def setUp(self):
    self.mockmyclass = MockMyClass()
    self.mocked = mock.patch(
        'path-to-my-file.MyClass',
        side_effect=self.mockmyclass.factory,
    )
    self.addCleanup(self.mocked.stop)
    self.mocked = self.mocked.start()

def test_something(self):
    # call code under test
    # then test with
    with self.mockmyclass['name-of-instance'] as inst:
        inst.start.assert_called_once()
        inst.stop.assert_called_once()
    # or test with
    self.mockmyclass['name-of-instance'].start.assert_called_once()

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2016-11-07
    • 1970-01-01
    • 2015-06-19
    • 2019-09-20
    • 2013-01-06
    • 1970-01-01
    • 1970-01-01
    • 2017-11-19
    相关资源
    最近更新 更多