Flask Application Testing with pytest Returns 404 for Routes

36 Views Asked by At

I'm currently working on unit testing a Flask application using pytest. However, when I attempt to test my routes (seemingly any of my routes), I consistently receive a 404 Not Found error, despite the route being defined and working as expected outside of the testing environment. I've checked the route registration and application factory setup but haven't found the cause of the issue.

Environment:

Flask 2.x pytest Python 3.x

config.py;

class Config:
SQLALCHEMY_TRACK_MODIFICATIONS = False

class DevelopmentConfig(Config):
    SQLALCHEMY_DATABASE_URI = 'sqlite:////path/to/development.db'
    DEBUG = True

class TestingConfig(Config):
    TESTING = True
    SQLALCHEMY_DATABASE_URI = 'sqlite:///:memory:'
    SQLALCHEMY_TRACK_MODIFICATIONS = False

app.py (simplified for brevity):

def create_app(config_class=DevelopmentConfig):
    app = Flask(__name__)
    app.config.from_object(config_class)  # Load the configuration from the provided class
    app.config['SQLALCHEMY_DATABASE_URI'] = config_class.SQLALCHEMY_DATABASE_URI
    app.config['SECRET_KEY'] = 'your_secret_key'

    # Initialize extensions with the app
    db.init_app(app)
    migrate.init_app(app, db)

    return app

app = create_app()

@app.route('/login', methods=['GET', 'POST'])
def login():
    # Route implementation
    return render_template('login.html')

test_app.py:

import pytest
from apis.stockpulse.app import create_app
from apis.stockpulse.config import TestingConfig

@pytest.fixture
def client():
    app = create_app(TestingConfig)
    with app.test_client() as client:
        with app.app_context():
            yield client

def test_login_get(client):
    response = client.get('/login')
    assert response.status_code == 200  # Fails with 404

Problem:

When running the test for the /login route, pytest returns a 404 status code, even though the route is correctly defined. I've attempted to debug by printing out all registered routes using app.url_map in the fixture, and it appears that only the default static route is registered.

Attempts to Solve:

Verified the route is correctly defined and works outside of testing. Ensured the TestingConfig is correctly applied, with TESTING = True. Checked for correct application context in tests. Attempted to explicitly print registered routes, which only showed the static route.

I'm at a loss for what's causing this issue. Could it be related to how I'm initializing the app for testing or perhaps something to do with the application context not properly encapsulating the route definitions? Any insights or suggestions would be greatly appreciated.

1

There are 1 best solutions below

0
tmt On

app defined in app.py and app created within the client fixture are two different Flask instances, and the instance from the fixture doesn't have the route applied via the @app.route decorator.

The solution is to use the same app object from app.py in your tests. That obviously means that you would need to change the way the application is configured so that it uses TestConfig. Unfortunately, the way I used to configure Flask when I was working with it is too complex to describe in this answer, and I don't have an alternative good solution on hand.

Therefore, take the following code sample rather as a crude fix and NOT as a solid example for production code:

import pytest
from app import app as real_app
from config import TestConfig

@pytest.fixture(scope="session")
def app():
    # WARNING: existing config keys not specified in TestConfig
    # would not be removed
    real_app.config.from_object(TestingConfig)
    real_app.test_request_context().push()

    return real_app

@pytest.fixture
def client(app):
    with app.test_client() as client:
        yield client

def test_login_get(client):
    response = client.get("/login")
    assert response.status_code == 200