1

我有一个用于从另一个非常简单的服务获取信息的对象。由于对象很简单并且初始化方法可以很容易地修补,我想我会尝试编写我的代码以实现超级可重用和可扩展。但是,唉,我不知道如何使它工作。下面的代码是相当不错的 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() 的响应以返回带有列表的多个值。但这似乎我会做大量的复制和粘贴,并且必须为每个小测试一遍又一遍地做同样的事情。如果这很简单,请原谅我,遗憾的是我只是在学习测试的艺术。有没有办法完成我想做的事情?也许我缺少一些超级简单的东西。或者,也许我只是无缘无故地走错了路。帮助?!

4

1 回答 1

1

睡了一觉之后,由于我以前没有找到其他几个类似的问题/答案,我能够以新鲜的眼光相对较快地弄清楚这一点。主要是这个,Python Mock Object with Method called Multiple Times

我不需要完全重建模块对象,我需要让 mock 为我做这件事,然后用 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
于 2018-11-28T15:16:25.290 回答