在Mock对象上使用方法

时间:2018-11-28 03:23:52

标签: python mocking pytest python-unittest python-unittest.mock

我有一个用于从另一个服务中获取信息的对象,这非常简单。由于对象很简单并且初始化方法很容易打补丁,我想我会尝试将代码编写为超级可重用和可扩展的。但是,a,我无法弄清楚如何使其工作。下面的代码是非常好的sudo代码,并且经过了超级简化,但应该可以理解这一点。

class SimpleClient:
    def __init__(self):
        pass
    def read(self, key, path='some/path'):
        return value_from_get_on_another_service

然后我有一个请求处理程序对象,该对象通过get_client()初始化客户端(如下所示)

def get_client():
    return SimpleClient()

然后,请求处理程序上的方法使用client.read()方法几次并使用不同的参数(第二个取决于第一个)。

对于我的测试,我认为我可以“修补” get_client方法以返回自己的简单对象,然后可以“常规”使用该对象,从而消除了对第三方服务的依赖,并实际上使用了从方法执行中检索到的值。我很失望地发现它不是那么容易和干净。测试模式如下所示。

class MockClient:
    def __init__(self, addr='someAddr', token='someToken'):
        pass

    def read(self, value, prefix):
        data = {}
        if prefix == 'path/1':
            data = self.p1_lookup(value)
        elif prefix == 'path/2':
            data = self.p2_lookup(value)

        return self.response_wrapper(data)

    def p2_lookup(self, key):
        data = {
        'key1': {
            'sub_key': {"55B3FE7D-9F43-4DD4-9090-9D89330C918A": "Dev2",
                        "7A1C2F4B-E91C-4659-A33E-1B18B0BEE2B3": "Dev"}
        }
    }

    return data.get(key, {})


@mock.patch('a.module.get_client')
def test_authorize_valid_request_no_body(mock_get_client):
    request = RequestMock()
    request.body = None
    handler = RequestHandler(Application(), request=request, logging_level='INFO')
    mock_get_client.return_value = MockClient()
    handler.authorize_request()
    assert handler.verified_headers is None
    assert handler.verified_body is None
    assert handler.user_authenticated is False

我已经看到我可以在哪里模拟实际client.read()的响应以返回带有列表的多个值。但这似乎就像我将要进行大量复制和粘贴,并且每次小测试都要一遍又一遍地做同样的事情。如果这很简单,请原谅我,可悲的是我只是在学习测试的技巧。有没有办法完成我想做的事情?也许有些超级简单的东西我不见了。或者,也许我没有充分的理由就完全走错了路。帮助吗?!

1 个答案:

答案 0 :(得分:1)

睡着后,新鲜的眼睛使我能够相对较快地解决这个问题,这要归功于我之前从未发现过的其他两个类似的问题/答案。主要是这个Python Mock Object with Method called Multiple Times

不需要完全重建模块对象,我需要让模拟为我自己做,然后使用side_effect属性覆盖其上的特定方法。因此,下面是经过清理的代码版本。

def read_override(value, prefix):
    lookup_data1 = {"lookup1": {'key1': 'value1'}}
    lookup_data2 = {'some_id': {'akey': {'12345678': 'DEV'}}

    data = {}
    if prefix == 'path1/1a':
        data = lookup_data1.get(value, {})
    elif prefix == 'path2/2a':
        data = lookup_data2.get(value, {})

    return {'data': data}

# Create a true Mock of the entire LookupClient Object
VAULT_MOCK = mock.Mock(spec=LookupClient)

# make the read method work the way I want it to with an "override" of sorts
VAULT_MOCK.read.side_effect = vault_read_override

然后测试看起来像这样...

@mock.patch('a.module.get_client')
def test_authorize_valid_request_no_body(get_client):
    get_client.return_value = VAULT_MOCK
    request = RequestMock()
    request.body = None
    handler = RequestHandler(Application(), request=request, logging_level='INFO')
    handler.authorize_request()
    assert handler.verified_headers is None
    assert handler.verified_body is None
    assert handler.user_authenticated is False