Getting to 100% Code Coverage With Flask Python Testing

Project Source Code

Get the project source code below, and follow along with the lesson material.

Download Project Source Code

To set up the project on your local machine, please follow the directions provided in the README.md file. If you run into any issues with running the project source code, then feel free to reach out to the author in the course's Discord channel.

Mocking HTTP Responses

We've tested the home blueprint, now let's add tests for the routes in the stock blueprint. Since this route actually issues real network requests to our stock ticker API, we can't be sure of it's consistency. For example, if we wanted to assert that the page actually displayed the stock price, we wouldn't be sure what string to look for because the real stock price is constantly changing. Instead, the approach we'll take is to "mock" or fake the data from the API to look like some data we've essentially hard-coded. If you've never done this before, this might sound odd but our goal with automated tests is not to test the external API, but rather our own code.

class MockPrice:
    @staticmethod
    def json():
        return {"price": 42.0}

Once we have our desired hard-coded result we need to monkey patch over some functions to return objects that we've already defined. This uses a built in fixture of PyTest called monkeypath which allows us to hard code the return value of functions. Here we're replacing requests.get with the mock_get function.

def test_stock_quote(client, monkeypatch):
    def mock_get(*args, **kwargs):
        return MockPrice()
    monkeypatch.setattr(requests, "get", mock_get)

    response = client.get(url_for('stock.view_stock', ticker="APPL"))
    assert response.status_code == 200
    assert b'Price: $42' in response.data
    assert b'portfolio' in response.data

In later chapters, we will cover how to do fine-grained mocking of HTTP requests.

Expecting Failure

If we want to test that our code actually does raise an exception, we can wrap the call in a pytest.raises(expected_exception) block.

def test_unknown_financials(client, monkeypatch):
    def mock_empty_response(*args, **kwargs):
        return MockNotFound()
    monkeypatch.setattr(requests, "get", mock_empty_response)

    with pytest.raises(KeyError):
        response = client.get(url_for('stock.financials', ticker="UNKNONW"))

You can similarly test the rest of the blueprint with some additional tests.

stock-app/tests/test_stock.py
from flask import url_for
import requests
import pytest

class MockPrice:
    @staticmethod
    def json():
        return {"price": 42.0}

class MockFinancials:
    @staticmethod
    def json():
        return {"financials": [{'date': '2019-01-01', "Revenue": '100.00',
                                'Revenue Growth': "0.1", "EPS": "2.2"}]}

This lesson preview is part of the Fullstack Flask: Build a Complete SaaS App with Flask course and can be unlocked immediately with a \newline Pro subscription or a single-time purchase. Already have access to this course? Log in here.

Unlock This Course

Get unlimited access to Fullstack Flask: Build a Complete SaaS App with Flask, plus 70+ \newline books, guides and courses with the \newline Pro subscription.

Thumbnail for the \newline course Fullstack Flask: Build a Complete SaaS App with Flask