↓Skip to main content
  1. Testing & Quality/

Integration Testing a Flask API with pytest

·378 words·2 mins·
Python Flask pytest API Testing
Ifarra
Author
Ifarra
Disturbing the peace!!
Table of Contents

A Flask integration test can call routes through Flask’s test client without starting a public server or binding a port. This checks routing, request parsing, response codes, and database behavior together. It builds on the Flask API introduction and the guidance on reliable REST APIs.

A test client sends a request through a Flask route to validation and a test database, then checks the response
Test the API boundary with isolated dependencies.

Make application setup configurable
#

An application factory lets a test pass a separate configuration rather than importing a globally configured server:

from flask import Flask
from flask_sqlalchemy import SQLAlchemy

db = SQLAlchemy()

def create_app(test_config=None):
    app = Flask(__name__)
    app.config.from_mapping(
        SQLALCHEMY_DATABASE_URI="sqlite:///books.db",
        SQLALCHEMY_TRACK_MODIFICATIONS=False,
    )
    if test_config:
        app.config.update(test_config)
    db.init_app(app)
    register_routes(app)
    return app

The test can use a temporary SQLite database and create its schema for that test run. For database features that SQLite does not model, use a disposable instance of the same database engine as production.

Send a request through the test client
#

import pytest
from app import create_app, db

@pytest.fixture
def client():
    app = create_app({
        "TESTING": True,
        "SQLALCHEMY_DATABASE_URI": "sqlite://",
    })
    with app.app_context():
        db.create_all()
        with app.test_client() as client:
            yield client
        db.session.remove()
        db.drop_all()

def test_create_book_returns_created_resource(client):
    response = client.post("/books", json={"title": "Dune", "author": "Frank Herbert"})

    assert response.status_code == 201
    assert response.json["title"] == "Dune"

The exact fixture depends on the project’s ORM and transaction strategy; the important point is that each test gets isolated state and cleans it up.

Check the contract, including invalid requests
#

Test the behavior clients rely on: required fields, unsupported content types, missing records, duplicate values, and permission failures. Assert stable status codes and response fields rather than matching an entire response when some values are generated dynamically.

Keep external services outside the test
#

Do not use production credentials or call a real payment, email, or third-party API from a normal test run. Inject a fake service or mock only the external boundary. Keep a smaller number of staging or contract tests for integrations that need the real service.

Common isolation mistakes
#

  • Reusing one database across tests without a reset or rollback.
  • Enabling debug mode in a test to reveal exceptions instead of inspecting the captured failure.
  • Asserting success while ignoring whether the resource was actually persisted.
  • Testing only the happy path and leaving request validation unexamined.

An integration test should be realistic about the Flask route and controlled about the resources surrounding it.

Related

Designing Reliable REST APIs with Flask
·803 words·4 mins
Python Flask REST API API Design Security
A practical follow-up to the Python REST API tutorial: make API contracts predictable and protect the service at its trust boundaries.
Parameterized Tests for Algorithms with pytest
·390 words·2 mins
Python pytest Algorithms Testing
Build a compact test matrix for a two-sum implementation and learn when example tests are more useful than property tests.
pytest Basics: Tests, Fixtures, and Clear Assertions
·1984 words·10 mins
Python pytest Testing Unit Testing
Write discoverable pytest functions, organize reusable state with fixtures, and make failures easy to understand.