How to Test Flask Applications

Routes, Templates, the Database and a tiny bit of load testing

Martin Thoma
Jul 19 · 6 min read
Image for post
Image for post

My Tiny Flask App

In order to make ensure correctness of the following parts, I’ve created a tiny Flask app. You can copy all files from GitHub.

example-app
├── mini_app
│ ├── __init__.py # make the folder a package
│ ├── app.py # contains the Flask app object
│ ├── config.py
│ ├── models.py # SQLAlchemy
│ └── templates/
└── tests
├── conftest.py # Test cfg
└── test_app.py # unit tests
export DB_HOST=localhost
export DB_DATABASE=books
export DB_USER=root
export DB_PASSWORD=you_wish

How to test Routes

Routes typically look like this in Flask:

How to deal with the Database

Most web applications have a database. When running tests, you want to be certain that the tests don’t hit the production database. At the same time, you want something like a database to be there.

Wait … what about testing the SQL Queries?

You might wonder now how to test the SQL queries. Testing that they work at all should not be necessary if you use SQLAlchemy. And I really recommend to use SQLAlchemy when you use Flask with a relational database. If your queries are too complex for that, you can have a look at Query Builders. Avoid using raw SQL. In most cases it should not be necessary.

Protected Routes

It’s pretty common that you have routes which are either protected by Basic Auth or need a form of login. You can essentially also set up a test account in the client fixture and login manually. This requires some work and depends on what exactly you’re doing for authentication.

Basic Access Authentication

You can provide the necessary credentials in the header within the test:

Flask Login

Flask-login is a pretty widespread plugin to handle user session management. They have a section about unit testing in which they suggest do set the configuration variable LOGIN_DISABLED to True .

Test Jinja2 Templates

Before we dive into testing Jinja2 Templates, let’s first recap a couple of things that can go wrong.

Problem 1: Empty Double Braces

You wanted to write something, got interrupted and now your template has {{}} in it. When Jinja tries to render this, you will get

jinja2.exceptions.TemplateSyntaxError: Expected an expression, got 'end of print statement'

Problem 2: Data Structure confusion

You assume number is a string, but it actually is an integer:

{% for digit in number %}
{{ digit }}
{% endfor %}

Problem 3.1: Typo in a Variable

Instead of {{ numbers }} you write {{ number }} . This is pretty bad as it actually does nothing. It is as if the variable number existed and was the empty string.

Problem 3.2: Forgetting to pass a Variable

You actually wanted to write number , but you forgot to pass it to the template. The effect is the same, but I think it’s an interesting different cause. This is what happens most often to me.

Status Code Testing

By calling a view and making sure that the assert rv.status_code == 200 you can already capture Problem 1 and 2:

Testing the Template Context

This one needed a lot of trail and error, but I finally managed to get some pytest fixtures with which you have a better control over the variables passed to the templates. I love it 😍

pytest-recording

pytest-recording is a pytest plugin which integrates vcr.py into pytest. There is also pytest-vcr and both plugins are not wide-spread. I think pytest-recording is better maintained as the author answered within 2 hours.

Block Network Access

This one is a potential live saver. Just decorate a test with @pytest.mark.block_network and you can be certain that everything runs locally. If something tries to make a network access, it is blocked and you get

RuntimeError: Network is disabled

Record Network Interactions

You can decorate a test with @pytest.mark.vcr() . Run pytest --record-mode=rewrite

Image for post
Image for post
Photo by Marc-Olivier Jodoin on Unsplash

Load Testing

Testing Flask apps is not only about testing the used functions and routes, but also about knowing your limits. You want to know what actually breaks and when it breaks when you get tons of users.

pip install locust
locust -f locustfile.py --host=https://your-website.com
Image for post
Image for post
I’ve chosen to simulate 100 users

What’s next?

In this series, we already had:

Analytics Vidhya

Analytics Vidhya is a community of Analytics and Data…

Martin Thoma

Written by

I’m a Software Engineer with focus on Data Science, Machine Learning. I have over 10 years of experience with Python.

Analytics Vidhya

Analytics Vidhya is a community of Analytics and Data Science professionals. We are building the next-gen data science ecosystem https://www.analyticsvidhya.com

Martin Thoma

Written by

I’m a Software Engineer with focus on Data Science, Machine Learning. I have over 10 years of experience with Python.

Analytics Vidhya

Analytics Vidhya is a community of Analytics and Data Science professionals. We are building the next-gen data science ecosystem https://www.analyticsvidhya.com

Welcome to a place where words matter. On Medium, smart voices and original ideas take center stage - with no ads in sight. Watch

Follow all the topics you care about, and we’ll deliver the best stories for you to your homepage and inbox. Explore

Get unlimited access to the best stories on Medium — and support writers while you’re at it. Just $5/month. Upgrade

Get the Medium app

A button that says 'Download on the App Store', and if clicked it will lead you to the iOS App store
A button that says 'Get it on, Google Play', and if clicked it will lead you to the Google Play store