Flask Unit Testing with Pytest and Coverage

FlaskFlaskBeginner
Practice Now

This tutorial is from open-source community. Access the source code

Introduction

In this lab, we will learn how to write unit tests for a Flask application. We will use pytest and coverage to test and measure our code. By the end of this lab, you will understand how to ensure your application works as expected and identify areas that need improvement.

Note: You need to create the code file yourself and run it in the environment. You can preview the Flask service status on Web 5000.


Skills Graph

%%%%{init: {'theme':'neutral'}}%%%% flowchart RL flask(("`Flask`")) -.-> flask/CoreConceptsGroup(["`Core Concepts`"]) flask(("`Flask`")) -.-> flask/DevelopmentToolsGroup(["`Development Tools`"]) flask(("`Flask`")) -.-> flask/DataHandlingGroup(["`Data Handling`"]) flask/CoreConceptsGroup -.-> flask/application_object("`Application Object`") flask/DevelopmentToolsGroup -.-> flask/blueprint_objects("`Blueprint Objects`") flask/DataHandlingGroup -.-> flask/incoming_request_data("`Incoming Request Data`") flask/DataHandlingGroup -.-> flask/response_objects("`Response Objects`") flask/CoreConceptsGroup -.-> flask/session_interface("`Session Interface`") flask/CoreConceptsGroup -.-> flask/test_client("`Test Client`") flask/DataHandlingGroup -.-> flask/application_globals("`Application Globals`") flask/CoreConceptsGroup -.-> flask/useful_internals("`Useful Internals`") flask/DevelopmentToolsGroup -.-> flask/command_line_interface("`Command Line Interface`") subgraph Lab Skills flask/application_object -.-> lab-136342{{"`Flask Unit Testing with Pytest and Coverage`"}} flask/blueprint_objects -.-> lab-136342{{"`Flask Unit Testing with Pytest and Coverage`"}} flask/incoming_request_data -.-> lab-136342{{"`Flask Unit Testing with Pytest and Coverage`"}} flask/response_objects -.-> lab-136342{{"`Flask Unit Testing with Pytest and Coverage`"}} flask/session_interface -.-> lab-136342{{"`Flask Unit Testing with Pytest and Coverage`"}} flask/test_client -.-> lab-136342{{"`Flask Unit Testing with Pytest and Coverage`"}} flask/application_globals -.-> lab-136342{{"`Flask Unit Testing with Pytest and Coverage`"}} flask/useful_internals -.-> lab-136342{{"`Flask Unit Testing with Pytest and Coverage`"}} flask/command_line_interface -.-> lab-136342{{"`Flask Unit Testing with Pytest and Coverage`"}} end

Install Pytest and Coverage

Firstly, we need to install pytest and coverage. These are testing and code measurement tools respectively. Run the following command in your terminal to install:

pip install pytest coverage

Setup and Fixtures

Next, we will set up test fixtures in a file called conftest.py. A fixture is a function that is run before each test function to which it is applied.

In this step, we will create a temporary database and populate it with some data for testing.

Here is the code to add in tests/conftest.py:

## tests/conftest.py

import os
import tempfile
import pytest
from flaskr import create_app
from flaskr.db import get_db, init_db

with open(os.path.join(os.path.dirname(__file__), 'data.sql'), 'rb') as f:
    _data_sql = f.read().decode('utf8')

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

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

    with app.app_context():
        init_db()
        get_db().executescript(_data_sql)

    yield app

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

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

@pytest.fixture
def runner(app):
    return app.test_cli_runner()

Write Tests for Factory

Next, we will write tests for the factory function which is responsible for creating the Flask application. These tests ensure that the application behaves as expected based on the configuration passed to it.

Here is the code to add in tests/test_factory.py:

## tests/test_factory.py

from flaskr import create_app

def test_config():
    assert not create_app().testing
    assert create_app({'TESTING': True}).testing

def test_hello(client):
    response = client.get('/hello')
    assert response.data == b'Hello, World!'

Test Database Connection

After testing the factory, we will test the database connection. These tests ensure that the database connection is established and closed as expected.

Here is the code to add in tests/test_db.py:

## tests/test_db.py

import sqlite3
import pytest
from flaskr.db import get_db

def test_get_close_db(app):
    with app.app_context():
        db = get_db()
        assert db is get_db()

    with pytest.raises(sqlite3.ProgrammingError) as e:
        db.execute('SELECT 1')

    assert 'closed' in str(e.value)

Test Authentication

Next, we will write tests for user authentication. These tests ensure that users can log in and log out as expected, and that appropriate error messages are displayed when necessary.

Here is the code to add in tests/test_auth.py:

## tests/test_auth.py

import pytest
from flask import g, session
from flaskr.db import get_db

def test_login(client, auth):
    assert client.get('/auth/login').status_code == 200
    response = auth.login()
    assert response.headers["Location"] == "/"

    with client:
        client.get('/')
        assert session['user_id'] == 1
        assert g.user['username'] == 'test'

Test Blog Posts

Finally, we will write tests for blog posts. These tests ensure that users can create, update, and delete blog posts as expected.

Here is the code to add in tests/test_blog.py:

## tests/test_blog.py

import pytest
from flaskr.db import get_db

def test_create(client, auth, app):
    auth.login()
    assert client.get('/create').status_code == 200
    client.post('/create', data={'title': 'created', 'body': ''})

    with app.app_context():
        db = get_db()
        count = db.execute('SELECT COUNT(id) FROM post').fetchone()[0]
        assert count == 2

Run the Tests

Now that we have written our tests, we can run them using the pytest command:

pytest

To measure the code coverage of your tests, use the coverage command to run pytest:

coverage run -m pytest

You can view a simple coverage report in the terminal with the following command:

coverage report

Summary

In this lab, we have learnt how to write unit tests for a Flask application using pytest and coverage. These tools help us ensure our application works as expected and identify areas that need improvement. Writing tests for your code is a good practice as it helps you catch bugs before they become a problem.

Other Flask Tutorials you may like