Training Flask unit tests

Flask routes work until they stop working, usually at the worst possible time. Unit tests catch regressions before they reach production. Flask has a built-in test client that makes testing routes clean without spinning up a real server.

TL;DR: Write unit tests for Flask applications using the built-in test client: test routes, request handling, and use mocks for external dependencies.
Stack: Python, Flask, pytest, Flask test client
Level: Intermediate
Reading time: ~20 min

Setup

pip install pytest coverage pytest-cov flask

Example 1: Simple function tests

def get_hero_name(hero_id):
    heroes = {1: 'Iron Man', 2: 'Captain America', 3: 'Thor', 4: 'Hulk', 5: 'Black Widow'}
    return heroes.get(hero_id, 'Unknown Hero')

def is_avenger(hero_name):
    avengers = {'Iron Man', 'Captain America', 'Thor', 'Hulk', 'Black Widow'}
    return hero_name in avengers
from marvel import get_hero_name, is_avenger

def test_get_hero_name():
    assert get_hero_name(1) == 'Iron Man'
    assert get_hero_name(6) == 'Unknown Hero'

def test_is_avenger():
    assert is_avenger('Iron Man') is True
    assert is_avenger('Spider-Man') is False
pytest example1/test_marvel.py

Example 2: Tests with classes and coverage

pytest example2/test_marvel.py --cov=. --cov-report term-missing

Example 3: Mocking external HTTP requests

from unittest.mock import patch

@patch('marvel.requests.get')
def test_fetch_hero_info(mock_get):
    mock_get.return_value.status_code = 200
    mock_get.return_value.json.return_value = {'name': 'Mocked Hero'}
    
    hero_name = Marvel.fetch_hero_info(1)
    assert hero_name == 'Mocked Hero'

Flask integration tests

import pytest
from app import app

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

def test_get_hero(client):
    response = client.get('/hero/1')
    data = response.get_json()
    assert response.status_code == 200
    assert data['hero_name'] == 'Iron Man'

def test_calculate_sum(client):
    response = client.post('/calculate', json={'n1': 5, 'n2': 10, 'operation': 'sum'})
    assert response.status_code == 200
    assert response.get_json()['result'] == 15

def test_calculate_invalid_operation(client):
    response = client.post('/calculate', json={'n1': 5, 'n2': 11, 'operation': 'subtract'})
    assert response.status_code == 400
    assert response.get_json()['error'] == 'Invalid operation'

conftest.py fix for imports

import sys, os
sys.path.insert(0, os.path.abspath(os.path.join(os.path.dirname(__file__), '..')))

What you’ve built

A Flask testing reference covering simple function tests, class-based tests, mocked HTTP calls, and integration tests using the Flask test client.

Next steps

  • Use app.config[“TESTING”] = True and a separate test database to isolate tests from development data.
  • Test all HTTP methods, status codes, response bodies, and error cases. Don’t just test the happy path.
  • Use conftest.py fixtures for the Flask app instance and database setup so they are shared across all test files.

Questions or feedback? Find me on LinkedIn or GitHub.

Leave a Comment