Testing Your Flask Web Application With PyTest - Part 4

in STEMGeeks4 months ago

Or Test Any Web Application With PyTest

Welcome back to our next installment in our Python coding introduction series. We have been working on web development, specifically using the Flask web framework with this being the fourth part of our Flask series, but the 16th article we have in working with Python code. Although we are testing our Flask web application we have created, we are using PyTest which can be used for any web application you would like to test.

The previous article in our Flask series can be seen here
For the complete series in getting started with Python, see here

I am trying to make a conscious effort to improve my testing as this is something I usually push to the side. In this article, we will be showing you the basic of implementing Pytest as part of your automated testing of your web applications.

We will just be covering the basics, but if your are interested in learning more about Pytest, one of the best places to start is the official site at:
https://docs.pytest.org/en/latest/

Python actually has a built in framework for testing called unittest which is a great choice and can be used to perform your testing, but I prefer to use Pytest as I find it to be clearer to define the tests. It is not built into Python like unittest is, which means you need to install the module before you can start working with it. This does mean the module is developed with changes released a lot more quickly than what a built in module like unittest would have.

Lets Start With Some Pytest Unit Tests


As with the rest of these articles, we've been using actual work examples to demonstrate and explain how to use the different features and modules being presented. We will do exactly the same below.

If you have not been working with the "flasking" app, clone the application from our previous example and you will be set up to run the following tests.

Make sure that you are able to run the app, by moving into the root directory off the application, activate your virtual environment and verify the app runs successfully with the Flask development web server.

If you are starting from scratch, run the following commands to get started:

# Clone the repository:
git clone https://github.com/vincesesto/python_study.git

# Move into the root directory:
cd article_15/

# Create a virtual environment named venv
python3 -m venv venv

# Activate the virtual environment
source venv/bin/activate

# Install all of the requirements:
pip3 install -r requirements.txt

# Export where the app.py script is
export FLASK_APP = "app.py"

# Verify the flask web server runs without errors
flask run

As we mentioned earlier, Pytest is not part of the core Python installation, so we will need to install it using pip3, as we have in the following command:

pip install pytest

You can also add it to your requests.txt file as we have in the example below:

Click==7.0
Flask==1.1.1
itsdangerous==1.1.0
Jinja2==2.10.3
MarkupSafe==1.1.1
Werkzeug==0.16.0
pytest==6.2.4

To start creating tests, we will set up a specific directory to hold all of our test scripts. Start with the unit tests by creating a tests directory and a unit directory inside the tests diretory:

mkdir -p tests/unit

As we will be running these test from the root of the app, we need to set up blank init.py scripts in the tests and unit directory to make sure the tests will be able to import the app as part of the test run, so create these files now:

touch tests/__init__.py
touch tests/unit/__init__.py

Pytest will look for all files that start with "test" or "test_" or "_test". As well each of the functions in your tests also need to start with the word "test", or if you create a class "Test". We will start with a basic test of the app index.html page, so start by creating the test_app.py file in the tests/unit directory:

touch tests/unit/test_app.py

Open the tests/unit/test_app.py file with your text editor and add in the following test code:

"""
This file (test_app.py) contains the unit tests for the app.py file.
"""
from app import app

def test_index_page():
    """
    GIVEN a Flask application
    WHEN the '/' page is requested (GET)
    THEN check the response is valid
    """
    with app.test_client() as client:
        response = client.get('/')
        assert response.status_code == 200
        assert b'Flasking App' in response.data
        assert b'Welcome to the Flasking App!' in response.data

This file includes a few sections but only one function that performs tests for us. The way the file is set up is as follows:

  1. We have a comment at the start of the script explaining what we want to achieve
  2. We import the app module from the root directory, that will be running our application in this instance
  3. We then create a function called test_index_page which its main purpose is to test the index page for us. We include comment at the start of the function outlining specifically what we want to achieve.
  4. The function uses the test_client() helper which creates a test version of the Flask application. The helper does the following for us:
    • It performs a GET request to the "/" location
    • It verifies the response from this request is a 200
    • It makes sure that even though we get a successful response we are seeing "Flasking App" and "Welcome to the Flasking App" as part of the response.

Just before we run the tests, make sure you have also exported the FLASK_APP environment variable as we did earlier in this article. If you have already done it, you won't need to do it again:

export FLASK_APP = "app.py"

To run our test all we now need to do, is go to the command line and from the root of the application, run the command pytest as we have below:

pytest

You should get a similar to the one below, and hopefully you have a 100% success rate for the tests. You could also run "pytest -v" to get more output, but currently when we only have one test, you shouldn't really need it:

======================== test session starts ========================
platform win32 -- Python 3.8.10, pytest-6.2.4, py-1.10.0, pluggy-0.13.1
rootdir: C:\flasking
collected 1 item

tests\unit\test_app.py .                                                                                         [100%]

======================== 1 passed in 0.85s ========================

Hopefully everything should have worked and our test should have completed successfully. We have listed this single test as a unit test because it performs a test against one single unit within out application. In the next part of this article, we will attempt to test our functionality with Pytest.

Creating Functional Tests


Functional tests will hopefully go a little further than simply testing if the page is returning a successfully and the content is correct. In the next section we will try to implement functional tests to verify the add_run page is successful and we can enter data into the page.

Get back into the working environment and create a new directory for the functional tests, which can be seperate from our unit tests.

mkdir tests/functional

Just as we did in the previous section we added the init.py file so that Pytest will recognice the tests as Modules. Do this for the functional tests directory as well:

cp .\tests\__init__.py .\tests\functional\

Create a file now for the add_run and list_run functionality we added to our app:

touch tests\functional\test_runs.py

Open the file with your text editor and add in the following details into the file...Make note that we have left out the tests for the test_get_add_run_page() function as this is almost exactly the same as the previous test we added in this article, but if you need to verify this, go back to our github page we mentioned earlier as we have the full test available in there:

"""
This file (test_runs.py) contains the functional tests for the app.py file.
"""
from app import app

def test_get_add_run_page():
    ...

def test_post_add_run_page():
    """
    GIVEN a Flask application
    WHEN the '/add_run' page is posted to (POST)
    THEN check that the user is redirected to the '/runs' page
    """
    with app.test_client() as client:
        response = client.post('/add_run',
                               data={'type_of_run': 'Easy',
                                     'number_of_kilometres': '5',
                                     'comments': 'Easy recovery run'},
                               follow_redirects=True)
        assert response.status_code == 200
        assert b'List of Runs' in response.data
        assert b'Type Of Run' in response.data
        assert b'Number of Kilometres' in response.data
        assert b'Comments' in response.data
        assert b'Easy' in response.data
        assert b'5' in response.data
        assert b'Easy recovery run' in response.data

The test_post_add_run_page function is where all the magic happens here. We will run through the function below, step by step:

  1. First we have a comment of all the things that are going to be tested, where we add items into the add_run form, and hopefully see a good result
  2. Once again we use the test_client() function from Pytest to perform a POST and include the data to use when the form is displayed.
  3. This time, it also follows the redirect to then load the "runs" page that will show results for us.
  4. We then perform a group of assert tests to verify the "runs" page is successfully displayed, it contains relevant content on the page, and also includes the data we entered in the "add_run" page.

We can now run Pytest to see if the tests pass or not. The pytest command will run all the tests in the tests directory but if we wanted to only run the functional tests we would run the command "pytest tests/functional/":

pytest

If all goes well, you should not see a response in the output similar to the one below as we have had all three of our tests pass successfully:

======================== test session starts ========================
platform win32 -- Python 3.8.10, pytest-6.2.4, py-1.10.0, pluggy-0.13.1
rootdir: C:\Users\m809728\Training\python_work\flasking
collected 3 items

tests\functional\test_runs.py ..                                                                                 [ 66%]
tests\unit\test_app.py .                                                                                         [100%]

======================== 3 passed in 1.59s ========================

Once again, we have done a lot of work to add unit tests and functional tests into our applications. We have been able to use Pytest to implement these into our app and we now have successful tests complete which should also give us some confidence that any changes we have made can now be deployed and are working correctly on our system.

Found this post useful? Kindly tap the up vote button below! :)

About The Author

I am a DevOps Engineer, Endurance Athlete and Author. As a DevOps Engineer I specialize in Linux and Open Source Applications. Particularly interested in Search Marketing and Analytic’s, and is currently developing my skills in devops, continuous integration, security, and development(Python).

Posted with STEMGeeks

Sort:  

I've not played with such test frameworks, but I probably should. It's easy for bugs to slip into your code as you change things.

!PIZZA

Yep definitely something I do not do enough either and why I am trying to incorporate it in this series or posts.

Connect

Trade


@run.vince.run! I sent you a slice of $PIZZA on behalf of @steevc.

Learn more about $PIZZA Token at hive.pizza (1/10)