Python API Development with FastAPI

Testing

Failed Login Test

In this article, we explore tests for failed login scenarios. After confirming that valid login attempts work as intended, it is crucial to verify that incorrect or incomplete credentials are properly handled by the API.


Basic Incorrect Login Test

The following initial test implementation verifies that providing an incorrect password results in the expected response from the API.

def test_incorrect_login(test_user, client):
    res = client.post(
        "/login", data={"username": test_user['email'], "password": "wrongPassword"}
    )
    # The API should return a 403 status code when the credentials are invalid.
    assert res.status_code == 403
    # Verify that the error message matches exactly.
    assert res.json().get('detail') == 'Invalid Credentials'

The login endpoint is implemented as shown below:

@router.post("/login", response_model=schemas.Token)
def login(user_credentials: OAuth2PasswordRequestForm = Depends(), db: Session = Depends(database.get_db)):
    user = db.query(models.User).filter(models.User.email == user_credentials.username).first()
    
    if not user:
        raise HTTPException(
            status_code=status.HTTP_403_FORBIDDEN, detail="Invalid Credentials"
        )
    
    if not utils.verify(user_credentials.password, user.password):
        raise HTTPException(
            status_code=status.HTTP_403_FORBIDDEN, detail="Invalid Credentials"
        )
    
    # (Token generation code would go here)

When executing the tests, you might see output similar to:

venv\lib\site-packages\aiofiles\os.py:10
venv\lib\site-packages\aiofiles\os.py:10
venv\lib\site-packages\aiofiles\os.py:10
C:\Users\sanjeev\documents\courses\fastapi\venv\lib\site-packages\aiofiles\os.py:10: DeprecationWarning: "@coroutine" decorator
tests/test_users.py::test_create_user PASSED
tests/test_users.py::test_login_user PASSED
tests/test_users.py::test_incorrect_login PASSED

Parameterized Tests for Multiple Scenarios

To thoroughly test the login functionality, we extend the tests to cover various failure cases. These include scenarios with a wrong email, wrong password, both credentials wrong, or even missing credentials. Pytest's parameterization feature allows us to efficiently test these different cases.

Note

Before adding parameterized tests, make sure you have imported Pytest in your test module:

import pytest

Consider the following parameterized test implementation:

@pytest.mark.parametrize("email, password, status_code", [
    # Wrong email with a valid password should return 403.
    ('[email protected]', 'password123', 403),
    # Valid email with wrong password should return 403.
    ('[email protected]', 'wrongpassword', 403),
    # Both email and password are incorrect; should return 403.
    ('[email protected]', 'wrongpassword', 403),
    # Missing email should fail schema validation and return 422.
    (None, 'password123', 422),
    # Missing password should fail schema validation and return 422.
    ('[email protected]', None, 422)
])
def test_incorrect_login(test_user, client, email, password, status_code):
    res = client.post(
        "/login", data={"username": email, "password": password}
    )
    assert res.status_code == status_code
    # When status_code is 403, you can optionally check for the error message.
    # For example:
    # if status_code == 403:
    #     assert res.json().get('detail') == 'Invalid Credentials'

Running the tests with this approach should yield an output similar to:

tests/test_users.py::test_create_user PASSED
tests/test_users.py::test_login_user PASSED
tests/test_users.py::test_incorrect_login PASSED

Any warnings, such as deprecation warnings from aiofiles, will be listed after the test summaries.


Summary

In summary, we now have a comprehensive set of tests that ensure the API responds appropriately when invalid credentials are provided. Whether the issue stems from an incorrect password, a wrong email, or missing fields, the API should return the correct status code and error details. These tests provide a robust baseline for your application's authentication logic, and they can be extended with additional assertions to verify other response fields as needed.

Happy testing!

Watch Video

Watch video content

Previous
Conftest Py File