Testing Flask routes do and don't exist


I'm creating a large number of Flask routes using regular expressions. I'd like to have a unit test that checks that the correct routes exist and that incorrect routes 404.

One way of doing this would be to spin up a local server and use urllib2.urlopen or the like. However, I'd like to be able to run this test on Travis, and I'm assuming that's not an option.

Is there another way for me to test routes on my application?

6/7/2018 12:04:29 PM

Accepted Answer

Use the Flask.test_client() object in your unittests. The method returns a FlaskClient instance (a werkzeug.test.TestClient subclass), making it trivial to test routes.

The result of a call to the TestClient is a Response object, to see if it as 200 or 404 response test the Response.status_code attribute:

with app.test_client() as c:
    response = c.get('/some/path/that/exists')
    self.assertEquals(response.status_code, 200)


with app.test_client() as c:
    response = c.get('/some/path/that/doesnt/exist')
    self.assertEquals(response.status_code, 404)

See the Testing Flask Applications chapter of the Flask documentation.

5/20/2019 11:48:51 PM

Martjin's answer surely solve your issue, but some times you don't have the time (or will) to mock all the components you call in a route you want to test for existence.

And why would you need to mock? Well, the call get('some_route') will first check for this route to exists and then ... it will be executed!

If the view is a complex one and you need to have fixtures, envs variables and any other preparation just for test if the route exists, then you need to think again about your test design.

How to avoid this:

You can list all the routes in your app. An check the one you're testing is in the list.

In the following example, you can see this in practice with the implementation of a site-map.

from flask import Flask, url_for

app = Flask(__name__)

def has_no_empty_params(rule):
    defaults = rule.defaults if rule.defaults is not None else ()
    arguments = rule.arguments if rule.arguments is not None else ()
    return len(defaults) >= len(arguments)

def site_map():
    links = []
    for rule in app.url_map.iter_rules():
        # Filter out rules we can't navigate to in a browser
        # and rules that require parameters
        if "GET" in rule.methods and has_no_empty_params(rule):
            url = url_for(rule.endpoint, **(rule.defaults or {}))
            links.append((url, rule.endpoint))
    # links is now a list of url, endpoint tuples


get a list of all routes defined in the app

Helper to list routes (like Rail's rake routes)

Licensed under: CC-BY-SA with attribution
Not affiliated with: Stack Overflow