5

I want to write tests for my FastAPI endpoints

example for my code:

from fastapi import FastAPI
from fastapi.testclient import TestClient

app = FastAPI()

@app.get("/todos")
async def get_todo_by_title(title: str,current_user: User = Depends(get_current_user))
    document = await collection.find_one({"title": title})
    return document

client = TestClient(app)

def test_get_todo_by_title():
    response = client.get("/todos")
    assert response.status_code == 200

What the best way to test my endpoints?

I want to use fake DB for testing, something like json file

db = {
todos: [...]
}
John
  • 73
  • 1
  • 5
  • The best way would be to just test the endpoints _with_ a test database in mongodb, so that you're sure they work as you expect. The next possibility would be to either mock your collection queries or create a fake that allows certain operations following the API of mongodb collections. A better solution would be to move your actual mongdb queries to a dedicated service that you inject into your views with Depends, then mock this service to give back a set of data as defined in a json file. But first: is all this indirection _really_ necessary? Start by running your tests with the real mongodb – MatsLindh Apr 26 '22 at 08:50
  • so if I run my tests on real MongoDB, you know how I ca to skip depends? use fake token for test? – John Apr 26 '22 at 10:59
  • Are you thinking about the user dependency? Or something else? – MatsLindh Apr 26 '22 at 11:00
  • about the user dependency – John Apr 26 '22 at 13:18
  • You can either make your application allow adding users and authenticate as the user (i.e. the real way), or you can use `app.dependency_overrides` to provide a custom function that returns a fake/static user in your tests. https://fastapi.tiangolo.com/advanced/testing-dependencies/ - `app.dependency_overrides[get_current_user] = lambda: return {'id': 1, 'username': 'foo'}` – MatsLindh Apr 26 '22 at 14:23

1 Answers1

4

It is not the best option to use fake data from a JSON file. Instead, you can use a testing DB (based on the env you are running your app through), or any other DB (dev, stg, ..etc), and delete your test data after running all unit tests.

Here is how to simply apply the latter approach in FastAPI;

  • Assume you have 3 simple tables (or collections) X, Y, and Z
  • MongoDB is the DB service
  • PyTest is the test engine

conftest.py

from pytest import fixture
from starlette.config import environ
from starlette.testclient import TestClient
from config.db_connection import database, X_collection, Y_collection, Z_collection


@fixture(scope="session")
def test_X():
    return {
        "_id": "10",
        "name": "test",
        "description": "test",
        "type": "single value",
        "crt_at": "2022-06-27T12:23:15.143Z",
        "upd_at": "2022-06-27T12:23:15.143Z"
    }

//test_Y and test_Z fixtures should be like test_X


@fixture(scope="session", autouse=True)
def test_client(test_X, test_Y, test_Z):
    import main
    application = main.app
    with TestClient(application) as test_client:
        yield test_client

    db = database
    //Here, delete any objects you have created for your tests
    db[X_collection].delete_one({"_id": test_X["_id"]})
    db[Y_collection].delete_one({"_id": test_Y["_id"]})
    db[Z_collection].delete_one({"_id": test_Z["_id"]})


environ['TESTING'] = 'TRUE'

Unit tests should look like the following sample.

test_X_endpoints.py

def test_create_X(test_client, test_X):
    response = test_client.post("/create_X_URI/", json=test_X)
    assert response.status_code == 201
    //Add assertions as needed

def test_list_X(test_client):
    response = test_client.get("/list_X_objects_URI/?page=1&size=1")
    assert response.status_code == 200
    //Add assertions as needed
MoAly
  • 76
  • 3