Testing Flask applications (code, database, views, flask config, and app context) with pytest

I love writing tests for my code but whenever starting in a new language or framework it's such a pain since getting the mocks and fixtures just right tends to be very language & framework specific. I searched and searched for a good pytest + flask configuration that would let me do unit and integration tests for the app and found some good pieces but nothing holistic.

Thorough testing of Flask apps

I wanted a pytest configuration that would give no side-effects between tests and provide a solid foundation for writing everything from unit to integration to database tests:

  • Mocked (monkey-patched) methods and classes for unit testing
  • Per-test Flask application context, letting you test things like oauth-filter-for-python-flask
  • Fast, in-memory SQLite database that is torn down between each test
  • REST client to test Flask APIs or views

pytest configuration

The code samples below are pretty generic but may require minor customization for your Flask application. I highly recommend you take a look at the flask-bones sample, which contains many best practices and this sample will work with it out of the box.

It assumes the use of the following modules available via pip: pytest, pytest-flask and pytest-mock


import pytest

from yourflaskmodule import create_app
from yourflaskmodule.config import test_config
from yourflaskmodule import db as _db

def app(request):
    """Test session-wide test `Flask` application."""
    app = create_app(test_config)
    return app

def _setup_app_context_for_test(request, app):
    Given app is session-wide, sets up a app context per test to ensure that
    app and request stack is not shared between tests.
    ctx = app.app_context()
    yield  # tests will run here

def db(app, request):
    """Returns session-wide initialized database"""
    with app.app_context():
        yield _db

def session(app, db, request):
    """Creates a new database session for each test, rolling back changes afterwards"""
    connection = _db.engine.connect()
    transaction = connection.begin()

    options = dict(bind=connection, binds={})
    session = _db.create_scoped_session(options=options)

    _db.session = session

    yield session


Here's an example of a base config class with the SQLite in-memory override:

class test_config(base_config):
    """Testing configuration options."""

    TESTING = True
    SQLALCHEMY_DATABASE_URI = 'sqlite:///memory'

Here's an example of a test making use of all the different features:

import pytest

    from flask import _app_ctx_stack as ctx_stack
except ImportError:
    from flask import _request_ctx_stack as ctx_stack

class TestCase:
    # set a Flask app config value using a pytest mark
    def test_foo(self, client, session):
        # set user identity in app context = {'sub': 'user1', 'tid': 'expected-audience'}

        # mock a class
        mocked_batch_client = mocker.patch('backend_class.BackendClient')
        assert(mocked_batch_client.return_value.list.return_value = ['a', 'b'])

        # test a view - it uses BackendClient (mocked now)
        resp = client.get('/api/items')
        data = json.loads(
        assert(len(data['results']) > 0)

        # insert data into the database - will get rolled back after test completion
        item = YourModel() = "bar"

Something new every day...

This one's my fault for skipping through the Python docs, but I recently found __getattr__ which is incredibly useful. One of those nifty features I missed from PHP when programming Python was the $$name variables, where I could name a variable based on the content of another or retrieve variables from the string (name) stored in another variable. __getattr__ does just this, and even better - You can call functions or methods with it too!

So here's my tip of the day: Both Python and __getattr__ are awesome Laughing