Skip to main content

How to Verify Flask Form Submissions and Database Inserts Using pytest?

pytest
In this tutorial, we’ll explore how to test a Flask application using pytest. We’ll use a simple contact form built with Flask-WTF and Flask-CKEditor to demonstrate testing form submissions and route responses. You’ll learn how to verify page redirects, form validation, and database inserts without affecting your real data. By the end, you’ll be able to write automated tests that make your Flask app more reliable and easier to maintain.

Prerequisite:

This tutorial is part of the Flask CKEditor Project Series.

📚 View the Complete Flask CKEditor Series

⬅ Previous Part                                                                                                               ➡ Next Part

Preliminary
Before I begin, please activate the virtual environment and install the required dependencies.
python -m venv venv
venv\Scripts\activate
pip install pytest
Then, we need to set up the file and folder structure, as below:
test.py
All files and folders remain the same as in the previous tutorial, except I have created a test.py for the purpose of testing the Flask application. For the current tutorial, test.py is where we are running our testing of an application and an SQLite3 database.

Therefore, an isolated test will provide cleaner and more modular testing.

Before we dive into details, let us figure out the following:

⚙️ What is Pytest?

Pytest helps to check if code works correctly by running small test functions and verifying expected results.

🚀 Why use Pytest?

  • Simple syntax (assert statements)
  • No boilerplate code needed
  • Supports fixtures (like your Flask client)
  • Great for testing web apps, APIs, and databases

Step 1: Test the Flask application
The app fixture sets up the Flask app and test client, and I have included the code below:
import pytest
from app import app

# ----------------- Fixture ----------------- 
@pytest.fixture
def client():
    # Enable testing mode, so Flask behaves appropriately for tests
    app.config['TESTING'] = True
    
    # Disable CSRF protection for testing purposes
    # This allows us to submit forms without a CSRF token
    app.config['WTF_CSRF_ENABLED'] = False
     
    # Provide the test client
    with app.test_client() as client:
        # Provide the client to the test functions
        yield client
        
# ----------------- Test Routes -----------------
def test_index(client):
    # Test the home page (GET '/')
    response = client.get('/')
    
    # Assert that the response returns HTTP 200 (OK)
    assert response.status_code == 200
    
# ----------------- Test Form Submission -----------------
def test_submit_form(client):
    # Test submitting the contact form (POST '/message')
    response = client.post('/message', data={
        'name': 'Kelvin',
        'email': 'test@example.com',
        'subscribe': 'y',
        'message': 'Hello world'
    # Follow the redirect to the thank you page
    }, follow_redirects=True)
    
    # Assert that the page loaded successfully
    assert response.status_code == 200
    
    # Assert that the response contains the success message
    # This ensures that form validation passed and redirect worked
    assert b"sent successfully" in response.data.lower()
To run this test, first I need to enable testing mode and disable CSRF protection, as CSRF will block testing and cause the test to fail. Then I have to run 2 tests; one is a routes test, and the other is a submission test.

The routes test checks whether the application loads to contact.html; if it does, it passes the first test. 

The second test is whether Pytest can imitate the user adding data to the respective fields, such as entering "Kelvin" in the name field. Next, it will be redirected to the thankyou.html, and if it succeeds, then it will pass the test. In the terminal, I run the code below:

pytest flask_ckeditor/test.py

2 tests

If the above test passes, I can now conduct further tests on the SQLite3 database.


Step 2: Test the SQLite3 database
A database fixture is used to set up a temporary SQLite database and tables in app.py, as shown below.

In app.py, let me change the SQLite3 connection from
import sqlite3
conn = sqlite3.connect("flask_ckeditor/message.db")
to the code set up below
conn = sqlite3.connect(app.config.get('DATABASE', 'flask_ckeditor/message.db'))
Then, I go ahead to set up the SQLite3 database test.
import pytest
from app import app
import sqlite3

# ----------------- Fixture -----------------
@pytest.fixture
def client(tmp_path):
    # Enable testing mode, so Flask behaves appropriately for tests
    app.config['TESTING'] = True
    
    # Disable CSRF protection for testing purposes
    app.config['WTF_CSRF_ENABLED'] = False
    
    # Use a temporary SQLite database for testing
    test_db = tmp_path / "test_message.db"
    app.config['DATABASE'] = str(test_db)
    
    # Initialize database table for test
    conn = sqlite3.connect(app.config['DATABASE'])
    c = conn.cursor()
    c.execute("""
        CREATE TABLE message (
            id INTEGER PRIMARY KEY AUTOINCREMENT,
            name TEXT NOT NULL,
            email TEXT NOT NULL,
            subscribe BOOLEAN,
            message TEXT NOT NULL
        )
    """)
    conn.commit()
    conn.close()
    
    with app.test_client() as client:
        yield client
  
# ----------------- Test Database Insert -----------------
def test_db_insert(client):
    """
    Test that submitting the form actually inserts data into the database
    """
    # Submit form data
    client.post('/message', data={
        'name': 'Kelvin',
        'email': 'test@example.com',
        'subscribe': 'y',
        'message': 'Hello world'
    }, follow_redirects=True)
    
    # Connect to the temporary test database
    conn = sqlite3.connect(app.config['DATABASE'])
    c = conn.cursor()
    
    # Retrieve all rows from the 'message' table
    c.execute("SELECT * FROM message")
    result = c.fetchall()
    conn.close()
    
    # Assert that exactly one row was inserted
    assert len(result) == 1
    
    # Assert that the data matches what was submitted
    row = result[0]
    assert row[1] == 'Kelvin'                 # name
    assert row[2] == 'test@example.com'       # email
    assert row[3] == 1                        # subscribe (True stored as 1)
    assert row[4] == 'Hello world'            # message
As I mentioned earlier, I will set up a temporary database as "test_message.db" to avoid corruption with the actual data in message.db. Then I need to create a database schema as usual. Finally, I will create the test data and add it to "test_message.db." If the result is showing one (inserted). Then the test was passed.

As in the previous test, I will run the same command line again for the current test; the result is shown in the terminal below.
1 test
The terminal showed that all three tests had been passed.

Final wrap-up:
In this tutorial, we explored how to test a Flask application using pytest, focusing on form handling and database interactions. We learned how to simulate GET and POST requests, handle Flask-WTF forms, and address common issues like CSRF protection during testing. By using a separate test database, we ensured our tests remain safe and do not affect real data. With these techniques, you can now confidently build reliable, maintainable Flask applications backed by proper automated testing. 🚀

Published: Mar 2026
Last Updated: Mar 2026

About the Author

Kelvin Loh is a Python developer focused on Flask, desktop applications, and business automation solutions. He shares practical tutorials and real-world coding projects to help developers and small businesses build useful applications.





Comments

Popular Posts

How to Design a Location Tracking Module for Desktop Business Systems Using Python?

This tutorial will show you how to create an interactive map application that allows users to input geographic coordinates and visualize locations on an interactive map using Python's Tkinter GUI framework enhanced with TTKBootstrap styling and the TkinterMapView widget.  Prerequisite: This tutorial is part of the standalone tutorial. 📚 View the  standalone tutorial Preliminary   Before I begin, it is recommended to activate the virtual environment before installing the relevant dependencies. python -m venv venv venv\Scripts\activate pip install ttkbootstrap tkintermapview As usual, we need to import the relevant module and set the root values from tkinter import * import ttkbootstrap as tb import tkintermapview from ttkbootstrap.dialogs import Messagebox root = tb.Window(themename='darkly') root.title('Find My Map') root.geometry("1200x1200") I would like to divide it into three sections.  a) Input Panel Layout,  The headers are mainly a label...

How to Create Flask Forms with CKEditor and Flask-WTF?

In this tutorial, we will be integrating Flask-WTF and Flask-CKEditor into your Flask application! You'll learn how to set up the editor, securely handle formatted HTML content, and create a seamless user experience that enhances any project that requires user-generated content. Let's get started! Prerequisite: This tutorial is part of the Flask CKEditor Project Series. 📚 View the Complete Flask CKEditor Series                                                                                                                                                  ➡ Next Part Preliminary Before I begin, it is recommended to activate the v...

How to Store Application Data with SQLite in Python?

  In the previous tutorial, I used TinyDB as our storage solution to keep application data in a simple JSON-based format. While TinyDB is lightweight and easy to use, many web applications require a more structured and scalable database system. In this tutorial, we will switch to SQLite , a powerful relational database that integrates well with Python and web frameworks. By using SQLite, you will learn how to store, manage, and query data in a more structured way for real-world applications. Prerequisite: This tutorial is part of the Flask CKEditor Project Series. 📚 View the Complete Flask CKEditor Series ⬅ Previous Part                                                                                                         ...