I'm currently trying to write some unit-tests for my fastAPI program.
The issue though is that some of the tests return a {"detail": "Not found"}
error while others work. I'm calling the tests via pytest
in the terminal on my local machine inside VSCode.
One of the tests that throws this error:
def test_test(client):
res = client.get("/1")
assert res.status_code == 200
I've hard coded the ID of the user that is created via a pytest fixture but even when I make the ID number dynamic (it will always be 1 since I only create 1 user and reset the DB after each test session) it has the same issue.
The route that I want to test:
@router.get("/{id}", response_model=schemas.UserResponse)
def get_user(id: int, db: Session = Depends(get_db)):
user = db.query(models.User).filter(models.User.id == id).first()
if not user:
raise HTTPException(
status_code=status.HTTP_404_NOT_FOUND,
detail=f"User with id: {id} does not exist.",
)
return user
Here are the tests that run before the one that breaks that both work flawlessly
def test_create_user(client):
res = client.post("/users/", json={"email": "[email protected]", "password": "password123"})
new_user = schemas.UserResponse(**res.json())
assert new_user.email == "[email protected]"
assert res.status_code == 201
def test_login_user(client, test_user):
res = client.post("/login", data={"username": test_user["email"], "password": test_user["password"]})
login_res = schemas.Token(**res.json())
payload = jwt.decode(login_res.access_token, settings.JWT_SECRET_KEY, algorithms=[settings.ALGORITHM])
id_ = payload.get("user_id")
assert id_ == test_user["id"]
assert login_res.token_type == "bearer"
assert res.status_code == 200
I have tried using the /
at the end of the route, or not. I have tried to call the tests directly from the directory and to debug via breakpoints. But I can't figure out why it can not find this route (plus 2 more but I'm guessing the issue is the same).
If you need more information please let me know I'd be happy to provide it.
Here are some of my confitest settings:
def override_get_db():
db = TestingSessionLocal()
try:
yield db
finally:
db.close()
app.dependency_overrides[get_db] = override_get_db
@pytest.fixture()
def session(scope="session"):
Base.metadata.drop_all(bind=engine)
Base.metadata.create_all(bind=engine)
db = TestingSessionLocal()
try:
yield db
finally:
db.close()
@pytest.fixture()
def client(session):
def override_get_db():
try:
yield session
finally:
session.close()
app.dependency_overrides[get_db] = override_get_db
yield TestClient(app)
@pytest.fixture
def test_user(client):
user_data = {"email": "[email protected]", "password": "password123"}
res = client.post("/users/", json=user_data)
assert res.status_code == 201
new_user = res.json()
new_user["password"] = user_data["password"]
return new_user
CodePudding user response:
Since you don't show where you mount the router in your users example it's hard to say, but my initial guess would be that you probably mount it under /users
, and are missing the /users/
part in front of 1
(since having user ids directly under / seems a bit weird).
Your test client makes a request for just /1, not /users/1 as I'd expect the layout to be.
def test_user_can_be_retrieved(client):
res = client.get("/users/1")
assert res.status_code == 200
You probably want to use your user fixture to create the user before retrieving it as well. I'd also skip returning the password from the API:
@pytest.fixture
def created_user(client):
user_data = {"email": "[email protected]", "password": "password123"}
res = client.post("/users", json=user_data)
assert res.status_code == 201
return res.json()
def test_user_can_be_retrieved(client, created_user):
res = client.get("/users/1")
assert res.status_code == 200
user = res.json()
assert user.username == created_user.username
# etc.