繁体   English   中英

使用 pytest 在测试烧瓶应用程序中使用模拟服务器

[英]use mock server in testing flask app using pytest

tl;dr我正在寻找测试在其路由下使用外部 API 的 Flask 应用程序的最佳实践,这可能是我必须在测试阶段使用模拟服务器模拟外部 API 的地方。


好的,我搜索pytest-mock和类似包的所有内容是我可以模拟一个函数并为测试断言准备结果。 但不幸的是,我找不到对我的情况的最佳实践解释:

我有一个 Flask 应用程序,在我使用外部 API 的一些路由下,我想使用模拟服务器替换那些真正的外部 API URL。 我得出了两个明显公平的解决方案:

测试时使用测试 URL

到目前为止,我可以通过app.config动态设置不同的 URL,以便在我的conftest.py我可以使用测试 URL 进行调用,实际上,我的应用程序将通过主 URL 启动。 现在我的问题是如何在pytest fixture设置阶段自动运行模拟服务器以占用测试 URL 本地主机端口。 我说自动是因为我可以天真地自己在后台运行一个手动模拟应用程序,这对于自动化测试来说是愚蠢和无用的

使用模拟函数而不是将请求发送到主 URL

我意识到我可以设置一个模拟程序,它是一个返回我想要的值的函数,并在调用我要测试的应用程序的同时使用它。 但关键是我想我的应用程序中使用模拟,而不是将返回值与模拟生成的值进行比较

那么我在哪里,我该怎么办?!

这是一个示例:

我的烧瓶应用程序:

app = Flask(__name__)

@app.route('/test')
def test():
    value = request.args.get('dummy_val')

    # using external APIs
    # This is where I need mock server
    output = requests.get(
         'http://external.api/some/url',
         params={'val':value}
    )
    return output*2

我的测试文件:

def test_can_double_value(test_client):
    result = test_client('/test', query_string={'dummy_val':'foo'})
    # test if status code == 200
    # test if string equals to something
    # blah blah blah

回答有点晚,但我刚才偶然发现了同样的问题。 以前的答案似乎正在解决实践中的问题,但是如果您从理论上考虑您的问题,我认为您有 2 个选择。

任何一个:

  • 在“请求”级别模拟接口,它工作正常,但如我之前建议的那样将您的测试代码暴露给实现细节
  • 在您的代码中为这些服务创建适配器,并在您的测试代码中用您的模拟/存根替换它们。
  • 使用测试装置,您可以使用以下要点中的代码为该过程启动自己的网络服务器: https : //gist.github.com/eruvanos/f6f62edb368a20aaa880e12976620db8

最后一种方法的好处非常简单。 您可以在测试时保持代码原样,没有存根/模拟,并在系统外创建模拟,在那里您可以自由控制模拟,而不必担心实际实现(像以前一样,您必须知道该服务是通过请求调用的)。

感谢jonrsharpe,我现在知道这可以使用响应库来完成。 现在如果我们想在夹具中模拟一个 API,可以按如下方式完成:

conftest.py

import response
from pytest import fixture

@fixture
def client():
    # add response
    responses.add(responses.GET,
        'http://mock.api/v',
        json={'key': 'value'},
        status=200
    )
    # do fixture stuff -> here it is yielding app test client
    app.config['TESTING'] = True
    client = app.test_client() 

    yield client

test_module.py

import response

@response.activate
def test_can_connect(client):
    result = client.get('/test')
    assert result.status_code == 200

并在app.py

@app.route('/test')
def test():
    res = requests.get('http://mock.api/v')
    # blah blah

responses文档中的评论建议我们在测试功能中添加响应。 但是因为我想将它添加到夹具中,所以我就是这样做的。

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM