Reputation: 503
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
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
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