简体   繁体   English

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

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

tl;dr I am looking for the best practice for testing a flask app which uses external APIs under its routes, and probably this is where I have to simulate the external APIs using mock servers in the testing stage. tl;dr我正在寻找测试在其路由下使用外部 API 的 Flask 应用程序的最佳实践,这可能是我必须在测试阶段使用模拟服务器模拟外部 API 的地方。


Ok, all I have got searching pytest-mock and similar packages is that I can mock a function and have the results prepared for test assertions.好的,我搜索pytest-mock和类似包的所有内容是我可以模拟一个函数并为测试断言准备结果。 But unfortunately I cannot find a best-practice explanation for my situation:但不幸的是,我找不到对我的情况的最佳实践解释:

I have a flask app, under some of the routes of which I use external APIs and I want to use mock servers replacing those real external API URLs.我有一个 Flask 应用程序,在我使用外部 API 的一些路由下,我想使用模拟服务器替换那些真正的外部 API URL。 I have come to two apparently fair solutions:我得出了两个明显公平的解决方案:

Using test URLs when testing测试时使用测试 URL

What I have got so far is that I can set different URLs dynamically by app.config so that in my conftest.py I can invoke using test URLs, and in reality, my app will be launched by the main URLs.到目前为止,我可以通过app.config动态设置不同的 URL,以便在我的conftest.py我可以使用测试 URL 进行调用,实际上,我的应用程序将通过主 URL 启动。 Now my question here is how I can run a mock server automatically in the pytest fixture setup stage to occupy the test URLs localhost ports.现在我的问题是如何在pytest fixture设置阶段自动运行模拟服务器以占用测试 URL 本地主机端口。 I said automatically because I can naively run a manual mock app in background myself, which is silly and useless for automated testing purposes我说自动是因为我可以天真地自己在后台运行一个手动模拟应用程序,这对于自动化测试来说是愚蠢和无用的

Using a mock function instead of sending the request to the main URL使用模拟函数而不是将请求发送到主 URL

I have realized that I can setup a mocker which is a function returning my desired value, and use it alongside calling my to-be-tested app.我意识到我可以设置一个模拟程序,它是一个返回我想要的值的函数,并在调用我要测试的应用程序的同时使用它。 but the point is I want to use the mock inside my app, not comparing the returning value with a mock generated value但关键是我想我的应用程序中使用模拟,而不是将返回值与模拟生成的值进行比较

So where am I and what should I do?!那么我在哪里,我该怎么办?!

Here is a sample:这是一个示例:

My flask app:我的烧瓶应用程序:

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

my test file:我的测试文件:

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

Bit late to answer, but I have stumbled across the same problem just now.回答有点晚,但我刚才偶然发现了同样的问题。 Previous answers seems to be solving the problem in practice, but if you think about your problem in theory, I think you have 2 options.以前的答案似乎正在解决实践中的问题,但是如果您从理论上考虑您的问题,我认为您有 2 个选择。

Either:任何一个:

  • mock the interface on 'requests' level, which works fine, but exposes your test code to implementation detail, as suggested before me在“请求”级别模拟接口,它工作正常,但如我之前建议的那样将您的测试代码暴露给实现细节
  • create adapters for these services in your code, and replace them with your mocks/stubs in your test code.在您的代码中为这些服务创建适配器,并在您的测试代码中用您的模拟/存根替换它们。
  • using a test fixture, you can start your own webserver for the process using the code from this gist: https://gist.github.com/eruvanos/f6f62edb368a20aaa880e12976620db8使用测试装置,您可以使用以下要点中的代码为该过程启动自己的网络服务器: https : //gist.github.com/eruvanos/f6f62edb368a20aaa880e12976620db8

The benefit of the last approach is quite straightforward.最后一种方法的好处非常简单。 You can leave your code as is when testing, no stubbing/mocking, and create the mocks outside your system, where you have free control over mocks, and don't have to worry about actual implementation (like before, you have to KNOW that the service is called via requests).您可以在测试时保持代码原样,没有存根/模拟,并在系统外创建模拟,在那里您可以自由控制模拟,而不必担心实际实现(像以前一样,您必须知道该服务是通过请求调用的)。

Thanks to jonrsharpe I now am aware that this can be done using responses library.感谢jonrsharpe,我现在知道这可以使用响应库来完成。 Now if we want to mock an API in the fixture, it can be done as follows:现在如果我们想在夹具中模拟一个 API,可以按如下方式完成:

in conftest.pyconftest.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

in test_module.pytest_module.py

import response

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

and in app.py并在app.py

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

Comment in responses documentation it is suggested that we add a response in the test function. responses文档中的评论建议我们在测试功能中添加响应。 But as I wanted to add it in a fixture, this is how I did it.但是因为我想将它添加到夹具中,所以我就是这样做的。

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

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