In the previous post we got a glimpse of pytest. In this post, we’ll be diving a little deeper. To do this, we’ll be build a basic Flask app.
Before we start coding though, let’s first set up our directory structure. Thankfully, pytest gives us a few examples in their Directory Structure and Good Practices pages. For the purposes of this post, we’ll use this structure:
. ├── requirements-dev.txt ├── requirements.txt ├── setup.py ├── src │ └── app.py └── tests ├── app_test.py └── conftest.py
Now that our structure is done, we need to fill in each of the files.
requirements.txt file, we’ll add
flask as it is a dependency for our project:
requirements-dev.txt file, we’ll add the following two lines. This makes it easy for devs to get started:
pytest -r requirements.txt
As we’re devs ourselves, we’ll need to install these dependencies. To do that, we issue the following command:
pip3 install -r requirements-dev.txt
Now we need to write the contents of our
setup.py file. As outlined in the pytest documentation, the following is necessary at a minimum:
1 2 3 from setuptools import setup, find_packages setup(name="FlaskApp", packages=find_packages())
Once that’s done, we need to “install” our app in “editable” mode. We do this by issuing the following command:
pip install -e .
Now that we’ve got all the prep out of the way, we’re ready to start coding our app. Let’s put the following code in
1 2 3 4 5 6 7 8 from flask import Flask app = Flask(__name__) @app.route('/') def index(): return 'OK'
To make sure our app works, let’s run the following commands:
FLASK_APP=src/app.py flask run
When we browse to
127.0.0.1:5000, we see an “OK” message returned. Great, it works!
Let’s now get to work on setting up testing for our app.
Note: The inspiration for our test app comes from the flask documentation.
conftest.py add the following:
1 2 3 4 5 6 7 8 9 10 from src.app import app import pytest @pytest.fixture def client(): app.testing = True app.config['DEBUG'] = True with app.test_client() as client: yield client
See this page for more information on
We’ve doing a few things in this block of code, so let’s pause for a moment and analyse it:
We’re using the
test_client()method - this enables us to test our Flask app
We’re then using a
yield- this ensures our client stays up while we’re using it for testing
app_test.py, we add the following:
1 2 3 def test_index(client): r = client.get('/') assert r.status == '200 OK'
As you can see, we’ve passed the
client fixture to our
test_index test. We’re then using the client’s
get method to call our
/ route. This results in a response_class object being returned. We then
assert that the status of the response to make sure it’s
assert is what makes or breaks our test. If the assertion is correct, the test passes. Otherwise it fails.
It’s now time to run our test:
$ pytest -v =============================== test session starts ================================ platform linux -- Python 3.8.0, pytest-5.4.3, py-1.8.1, pluggy-0.13.1 -- /home/wrobinson/Development/python-tdd/venv/bin/python cachedir: .pytest_cache rootdir: /home/wrobinson/Development/python-tdd collected 1 item tests/app_test.py::test_index PASSED [100%] ================================ 1 passed in 0.02s =================================
As always, if you have any questions or have a topic that you would like me to discuss, please feel free to post a comment at the bottom of this blog entry, e-mail at firstname.lastname@example.org, or drop me a message on Reddit (OzNetNerd).
Note: The opinions expressed in this blog are my own and not those of my employer.