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. 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:
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:
⚙️ 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 (
assertstatements) - 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
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.
import sqlite3
conn = sqlite3.connect("flask_ckeditor/message.db")
to the code set up belowconn = 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.
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. 🚀




Comments
Post a Comment