Rodrigo Vaamonde
Rodrigo Vaamonde

Reputation: 503

How to run flask along side my tests in PyTest?

The usual flags: I'm new to Python, I'm new to PyTest, I'm new to Flask.

I need to create some server independent tests to test an api which calls a third-party. I cannot access that api directly, but I can tell it what url to use for each third-party.

So what I want to do is to have a fake api running on the side (localhost) while I'm running my tests, so when the api that I'm testing needs to consume the third-parties, it uses my fake-api instead.

So I created the following app.py:

from flask import Flask
from src.fakeapi.routes import configure_routes

app = Flask(__name__)

configure_routes(app)

def start_fake_api():
    app.run(debug=True)

And my_test.py:

from src.fakeapi.app import start_fake_api

@start_fake_api()
def test_slack_call():

send_request_to_api_to_configure_which_url_to_use_to_call_third_party("http://127.0.0.1:5000/")

send_request_to_api_to_populate_table_using_third_party()

Now, this might be an oversimplified example, but that's the idea. My problem obviously is that once I run Flask the process just stays in stand by and doesn't continue with the tests.

I want to avoid having to depend on manually running the server before running the tests, and I want to avoid running my tests in parallel.

What's the best way to do this? Can I somehow execute app.py when I execute pytest? Maybe by altering pytest.ini somehow? Can I force a new thread just for the server to run?

Thanks in advance!

Upvotes: 1

Views: 976

Answers (2)

Evgeniy Blinov
Evgeniy Blinov

Reputation: 387

You can use my plugin for Pytest to do this.

Install it:

pip install flask_fixture

Define some routes in your conftest.py file:

from flask_fixture import endpoint

@endpoint('/')
def root():
    return 'some text'

And use a URL of a server in your tests as a fixture local_server_url:

import requests

def test_run_server(local_server_url):
    assert requests.get(local_server_url).text == 'some text'

Upvotes: 1

AKX
AKX

Reputation: 169416

I don't see a good reason to run a fake server, when you can instead use mock libraries such as requests-mock or responses to respond.

That said, if you really do need to run a real server, you could set up a session scoped fixture with a cleanup.

Adding autouse will make the tests automagically start the server, but you can leave that out and just invoke the fixture in your test, á la test_foo(fake_api)

Implementing the TODOed bit can be a little tricky; you'd probably need to set up the Werkzeug server in a way that you can signal it to stop; e.g. by having it wait on a threading.Event you can then raise.

@pytest.mark.fixture(scope="session", autouse=True)
def fake_api():
    app = ...
    port = random.randint(1025, 65535)  # here's hoping no one is on that port
    t = threading.Thread(target=lambda: app.run(debug=True, port=port))
    t.start()
    yield port
    # TODO: implement cleanly terminating the thread here :)

Upvotes: 2

Related Questions