Testing FastAPI endpoints using fastapi.testclient

This post shows you an example of how to use fastapi.testclient provided by FastAPI. This testclient will help anyone to write code level tests quickly.


Background

Recently, we have developed an internal application using FastAPI. I thought to document a quick testing approach which may help software engineers to write test cases quickly. Here I have used fastapi.testclient instead of the requests module as I wanted to explore it.


The application under test

Our application under test is developed using FastAPI and exposes certain API endpoints which will be used to send skype messages to the predefined channels. I will be taking only one sample method the purpose of this blog.

CULTURE_FILE = 'blah blah blah'
 
def get_messages_from_file(filename):
    "Return a list of culture related messages"
    lines = []
    with open(filename, 'r') as fp:
        lines = fp.readlines()
 
    return lines
 
@app.get("/message")
def get_message():
    "Return a random message"
    lines = get_messages_from_file(CULTURE_FILE)
    message = random.choice(lines)
 
    return {'msg':message.strip()}

Let us assume this code exists in a file called main.py.


Using fastapi.testclient

The testclient is a wrapper written using the requests module in Python, You will need to install the requests module to use test client. Our way of writing this test will involve the following steps:

1. Create a Test client.
2. Get the response from the client using the exposed endpoint.
3. Assert the status code for the response as 200.
4. Assert the response for non-empty messages.
5. Assert the response for the correct message taken from the file.


1. Create Test client

Create a new file called test_main.py and add the following lines:

"""
Test for the main page using fastapi.testclient.
"""
import os
import sys
from fastapi.testclient import TestClient
import main
from main import app
sys.path.append(os.path.dirname(os.path.dirname(os.path.abspath(__file__))))
 
# Declaring test client
client = TestClient(app)

At this stage, we have imported the necessary modules and created test client. Note that we have imported appfrom the main and parsing that while creating test client.


2. Get the response from the client using the exposed endpoint

Now, within the test_get_message() method, create a response using exposed endpoint

# Test for status code for get message
def test_get_message():
    "asserting status code"
    response = client.get("/message")

3. Assert the status code for the response as 200

You can assert the response is showing status code as 200

assert response.status_code == 200

4. Assert the response for non-empty messages

This step will ensure that we don’t get an empty message before validating the actual message from the file. We will create new method test_get_message_text().

# Test for asserting random message is in the file
def test_get_message_text():
    "asserting random message is in the file"
    response = client.get("/message")
    message = response.json()
    assert message['msg'] != ''

5. Assert the response for the correct message taken from the file

We will now check that message we got is really from the intended file. In this case CULTURE_FILE

# Test for asserting random message is in the file
def test_get_message_text():
    "asserting random message is in the file"
    response = client.get("/message")
    message = response.json()
    assert message['msg'] != ''
    with open(main.CULTURE_FILE, 'r') as file_handler:
        lines = [line.strip() for line in file_handler]
    assert message['msg'] in lines

Putting it all together

Here is how our test looks:

"""
Test for the main page using fastapi.testclient.
"""
import os
import sys
from fastapi.testclient import TestClient
import main
from main import app
sys.path.append(os.path.dirname(os.path.dirname(os.path.abspath(__file__))))
 
# Declaring test client
client = TestClient(app)
 
# Test for status code for get message
def test_get_message():
    "asserting status code"
    response = client.get("/message")
    assert response.status_code == 200
 
# Test for asserting random message is in the file
def test_get_message_text():
    "asserting random message is in the file"
    response = client.get("/message")
    message = response.json()
    assert response.json()["msg"] != ''
    with open(main.CULTURE_FILE, 'r') as file_handler:
        lines = [line.strip() for line in file_handler]
    assert message['msg'] in lines


If you are working on testing applications using FastAPI, do consider using fastapi.testclient for your tests. I hope this article would help you to build quick tests around test client.


10 thoughts on “Testing FastAPI endpoints using fastapi.testclient

  1. Hello, can you provide some examples for testing with FastAPI when we have to have authenticated user to collect data from the database?

      1. Thank you, but I need to write tests for accessing database (PostgreSQL) with a user that needs to be authenticated with a JWT token. I do not know how to write fixture for this kind of user. For example, I need a test for accessing endpoint language_school/language_test that only logged in user can access this endpoint, otherwise to get 401.

  2. As a true beginner, I would like you to show what I type at a command line to run the test or how to use some other tools to run the test you showed.

    1. Hi Lee,

      You can run the test using pytest such as python -m pytest test_file_name.py -sv in your python virtual environment. Note that, you will need to install dependencies such as pytest, fastapi testclient in your virtual environment.

      I will update my screen shot in the blog.

      Regards,
      Rahul

  3. Hi, I would like to find out how do I properly create a unit for post request requiest to create user. I would like to pass ‘name’ as parameter. At the moment this is what I have:

    response = client.post(“/user?name=the_user”)
    assert response.status_code == 200

    This works and my unit test passes but I want a better way to replace “?name=the_user”

    1. Hey Sizwe,
      You may try below piece of code and see if that works for you.
      url_params = {“name”:”the_user”}
      response = client.post(‘/user’,params=url_params)
      assert response.status_code == 200

  4. Hello, i have so many endpoints to test. is there a quick way to test everything? for example, testing all endpoints result in 200.

    1. Hi,
      Check for Synthetic monitoring tools for API/HTTP monitoring, there are a few tools that will help check the status code alone in a x interval, we are on the lookout for a tool that will help us setup API monitoring at one of our client too.

Leave a Reply

Your email address will not be published. Required fields are marked *