Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Spin up a local flask server for testing with pytest

I have the following problem.

I'd like to run tests on the local flask server before deploying to production. I use pytest for that. My conftest.py looks like that for the moment:

import pytest
from toolbox import Toolbox
import subprocess


def pytest_addoption(parser):
    """Add option to pass --testenv=local to pytest cli command"""
    parser.addoption(
        "--testenv", action="store", default="exodemo", help="my option: type1 or type2"
    )


@pytest.fixture(scope="module")
def testenv(request):
    return request.config.getoption("--testenv")


@pytest.fixture(scope="module")
def testurl(testenv):
        if testenv == 'local':
            return 'http://localhost:5000/'
        else:
            return 'https://api.domain.com/'

This allows me to test the production api by typing the command pytest and to test a local flask server by typing pytest --testenv=local

This code WORKS flawlessly.

My problem is that I have to manually instantiate the local flask server from the terminal each time I want to test locally like this:

source ../pypyenv/bin/activate
python ../app.py

Now I wanted to add a fixture that initiates a terminal in the background at the beginning of the tests and closes the server down after having finished testing. After a lot of research and testing, I still cannot get it to work. This is the line I added to the conftest.py:

@pytest.fixture(scope="module", autouse=True)
def spinup(testenv):
    if testenv == 'local':
        cmd = ['../pypyenv/bin/python', '../app.py']
        p = subprocess.Popen(cmd, shell=True)
        yield
        p.terminate()
    else:
        pass

The errors I get are from the requests package that says that there is no connection/ refused.

E requests.exceptions.ConnectionError: HTTPConnectionPool(host='localhost', port=5000): Max retries exceeded with url: /login (Caused by NewConnectionError(': Failed to establish a new connection: [Errno 111] Connection refused',))

/usr/lib/python3/dist-packages/requests/adapters.py:437: ConnectionError

This means for me that the flask server under app.py is not online. Any suggestions? I am open to more elegant alternatives

like image 811
Si Mon Avatar asked Oct 16 '25 20:10

Si Mon


2 Answers

For local testing the Flask test_client is a more elegant solution. See the docs on Testing. You can create a fixture for the test_client and create test requests with that:

@pytest.fixture
def app():
    app = create_app()
    yield app
    # teardown here

@pytest.fixture
def client(app):
    return app.test_client()

And use it like this:

def test_can_login(client):
    response = client.post('/login', data={username='username', password='password'})
    assert response.status_code == 200

If the only problem are the manual steps, maybe consider a bash script that does your manual setup for you and after that executes pytest.

like image 73
ekuusela Avatar answered Oct 18 '25 16:10

ekuusela


I am using the following for this purpose so that testing configuration is also preserved in the test server

@pytest.fixture(scope="session")
def app():
    db_fd, db_path = tempfile.mkstemp()

    app = create_app({
        'TESTING': True,
        'DATABASE': db_path
    })

    yield app

    os.close(db_fd)
    os.unlink(db_path)

from flask import request
def shutdown_server():
    func = request.environ.get('werkzeug.server.shutdown')
    if func is None:
        raise RuntimeError('Not running with the Werkzeug Server')
    func()

@pytest.fixture
def server(app):
    @app.route('/shutdown',methods=('POST',))
    def shutdown():
        shutdown_server()
        return 'Shutting down server ...'

    import threading    
    t = threading.Thread(target=app.run)
    yield t.start()

    import requests
    requests.post('http://localhost:5000/shutdown')

References

  • https://flask.palletsprojects.com/en/1.1.x/tutorial/tests/
  • How do I terminate a flask app that's running as a service?
  • How to stop flask application without using ctrl-c
like image 40
S.aad Avatar answered Oct 18 '25 17:10

S.aad