Testing with TestClient & pytest
supertest / Jest → TestClient + pytest
Section titled “supertest / Jest → TestClient + pytest”In Node you use supertest to make HTTP requests against your Express or NestJS app in-process, and Jest as the test runner. In FastAPI you use TestClient (from Starlette, bundled with FastAPI) for in-process HTTP requests, and pytest as the test runner.
// Jest + supertest — app.test.tsimport request from 'supertest';import { app } from '../src/app';
describe('GET /items/:id', () => { it('returns an item', async () => { const res = await request(app).get('/items/1'); expect(res.status).toBe(200); expect(res.body.id).toBe(1); });
it('returns 404 for unknown id', async () => { const res = await request(app).get('/items/9999'); expect(res.status).toBe(404); });});# pytest + TestClient — test_items.pyfrom fastapi.testclient import TestClientfrom app.main import app
client = TestClient(app)
def test_get_item(): response = client.get("/items/1") assert response.status_code == 200 assert response.json()["id"] == 1
def test_get_item_not_found(): response = client.get("/items/9999") assert response.status_code == 404Testing POST with a request body
Section titled “Testing POST with a request body”// supertest POSTit('creates an item', async () => { const res = await request(app) .post('/items') .send({ name: 'Widget', price: 9.99 }) .set('Content-Type', 'application/json');
expect(res.status).toBe(201); expect(res.body.name).toBe('Widget');});
it('rejects invalid input', async () => { const res = await request(app) .post('/items') .send({ name: '', price: -1 }); expect(res.status).toBe(422);});# TestClient POSTdef test_create_item(): response = client.post( "/items", json={"name": "Widget", "price": 9.99}, ) assert response.status_code == 201 assert response.json()["name"] == "Widget"
def test_create_item_invalid(): response = client.post( "/items", json={"name": "", "price": -1}, ) assert response.status_code == 422Overriding dependencies in tests
Section titled “Overriding dependencies in tests”The key testing pattern in FastAPI is app.dependency_overrides — replacing real dependencies (like a database session) with test doubles. This is the equivalent of Jest’s jest.mock() or NestJS’s overrideProvider().
// NestJS — override a provider in testsconst moduleRef = await Test.createTestingModule({ providers: [ItemsService],}).overrideProvider(DatabaseService).useValue({ findById: jest.fn().mockResolvedValue({ id: 1 }) }).compile();# FastAPI — dependency_overridesfrom fastapi.testclient import TestClientfrom app.main import appfrom app.database import get_db
# A fake DB session for testsdef override_get_db(): yield FakeDBSession()
app.dependency_overrides[get_db] = override_get_db
client = TestClient(app)
def test_get_item_with_fake_db(): response = client.get("/items/1") assert response.status_code == 200
# Restore after tests (or use a pytest fixture)app.dependency_overrides.clear()pytest fixtures for setup/teardown
Section titled “pytest fixtures for setup/teardown”pytest’s @pytest.fixture is the equivalent of Jest’s beforeEach / afterEach. A conftest.py file shares fixtures across test files — like a Jest global setup file.
// Jest beforeEach / afterEachlet client: TestClient;
beforeAll(async () => { const app = await NestFactory.create(AppModule); await app.init(); client = app.getHttpServer();});
afterAll(async () => { await app.close(); });# pytest — conftest.py fixturesimport pytestfrom fastapi.testclient import TestClientfrom app.main import appfrom app.database import Base, engine, get_dbfrom sqlalchemy import create_enginefrom sqlalchemy.orm import sessionmaker
TEST_DB_URL = "sqlite:///./test.db"test_engine = create_engine(TEST_DB_URL, connect_args={"check_same_thread": False})TestSession = sessionmaker(bind=test_engine)
@pytest.fixture(scope="module")def client(): Base.metadata.create_all(bind=test_engine) def override_db(): db = TestSession() try: yield db finally: db.close() app.dependency_overrides[get_db] = override_db with TestClient(app) as c: yield c Base.metadata.drop_all(bind=test_engine)Run this locally — needs FastAPI + httpx + pytest. Install:
pip install httpx pytest pytest-covPlayground is skipped: TestClient requires a full FastAPI app and HTTP lifecycle.