Python API Development with FastAPI

Testing

Get One Post Test

In this article, we extend our testing suite by verifying the retrieval of individual posts. We start by ensuring that unauthorized users are prevented from accessing posts and then validate both valid and invalid post retrieval scenarios using an authorized client.

–––––––

Unauthorized Access Tests

First, we verify that an unauthorized user cannot retrieve all posts. The following code snippet demonstrates this test:

def test_unauthorized_user_get_all_posts(client, test_posts):
    res = client.get("/posts/")
    assert res.status_code == 401

Next, we ensure that an unauthorized user cannot access a single post. We use the ID of the first post in our test data. Depending on your data structure, the ID may be accessed as a dictionary key or as an object attribute. Here, we assume it is provided by the SQLAlchemy model as an attribute:

def test_unauthorized_user_get_one_post(client, test_posts):
    res = client.get(f"/posts/{test_posts[0].id}")
    assert res.status_code == 401

–––––––

Handling Non-Existent Posts

It is essential to confirm that the service returns a 404 error when a request is made for a post which does not exist. In the example below, we simulate a request for a post with an ID that is very unlikely to exist (e.g., 88888):

def test_get_one_post_not_exist(authorized_client, test_posts):
    res = authorized_client.get(f"/posts/88888")
    assert res.status_code == 404

Note

When running these tests, you might encounter warnings similar to the following. These warnings do not affect the test outcomes.

venv\lib\site-packages\aiofiles\os.py:10: DeprecationWarning: "@coroutine" decorator is deprecated since Python 3.8, use "async def" instead
-- Docs: https://docs.pytest.org/en/stable/warnings.html

–––––––

Retrieving a Valid Post

Finally, we test the retrieval of a valid post with an authorized client. The test includes printing the returned JSON response (useful for debugging) and then validating the response against our Pydantic schema. Note that the JSON response contains a "Post" property nested within the returned data; thus, the details are accessed from this nested object.

def test_get_one_post(authorized_client, test_posts):
    res = authorized_client.get(f"/posts/{test_posts[0].id}")
    # Optional: Print the JSON response for debugging purposes.
    # print(res.json())
    post = schemas.PostOut(**res.json())
    # Confirm that the retrieved post's ID and content match the expected test post.
    assert post.Post.id == test_posts[0].id
    assert post.Post.content == test_posts[0].content

For your reference, the Pydantic schema for a post is defined as follows:

class Post(PostBase):
    id: int
    created_at: datetime
    owner_id: int
    owner: UserOut

    class Config:
        orm_mode = True

This configuration ensures that the data returned by SQLAlchemy is compatible with our schema, which is why we access the nested "Post" property in our validation assertions.

–––––––

Test Run Summary

When you run the tests with pytest, you will see an output similar to this:

venv\lib\site-packages\aiofiles\os.py:10: DeprecationWarning: "@coroutine" decorator is deprecated since Python 3.8, use "async def" instead
-- Docs: https://docs.pytest.org/en/stable/warnings.html
...
5 passed, 5 warnings in 1.65s

This output confirms that the tests for unauthorized access, handling non-existent posts, and retrieving a valid post are functioning as expected.

––––––– In the next article, we will explore testing the creation of a post.

Watch Video

Watch video content

Previous
Unauthorized Get Posts