Skip to content

Exercise: Add Type Hints and Write Your First Test

Three short pieces: annotate two functions you've already written, define a Pydantic model, and prove it works with pytest.

Part 1: Type Hints

Open the chapter 1 exercise file where you wrote is_overdue and next_status. Add full type hints to both.

Just annotate the parameters and return types. date and TaskStatus are already imported at the top of the file from the chapter 1 exercise.

The expected signatures:
Python
def is_overdue(due_date: date | None, status: TaskStatus) -> bool: ...


def next_status(status: TaskStatus) -> TaskStatus: ...

Part 2: A Pydantic Model

In a fresh file called computer.py at your project root, define a Pydantic model called Computer:

  • brand: str
  • ram_gb: int
  • hard_drive_gb: int

Create a valid instance:

Python
laptop = Computer(brand="apple", ram_gb=16, hard_drive_gb=512)
print(laptop)

Then try to create an invalid one with a string where Pydantic expects an integer:

Python
try:
    bad = Computer(brand="apple", ram_gb="thirty two", hard_drive_gb=512)
except ValidationError as e:
    print(e)

Read the error message. Pydantic tells you exactly which field failed, what type it expected, and what it got.

Part 3: Tests

Create tests/test_computer.py. Write three tests:

  1. test_computer_valid: construct a valid Computer and assert each of the three fields holds the value you passed in.
  2. test_computer_rejects_string_ram: assert that passing a string for ram_gb raises ValidationError. Use pytest.raises.
  3. test_computer_requires_brand: assert that constructing a Computer without a brand argument raises ValidationError.

Run the suite:

shell
uv run pytest

You should see three dots and 3 passed.

Solution

computer.py:

Python
from pydantic import BaseModel, ValidationError


class Computer(BaseModel):
    brand: str
    ram_gb: int
    hard_drive_gb: int


if __name__ == "__main__":
    laptop = Computer(brand="apple", ram_gb=16, hard_drive_gb=512)
    print(laptop)

    try:
        bad = Computer(brand="apple", ram_gb="thirty two", hard_drive_gb=512)
    except ValidationError as e:
        print(e)

tests/test_computer.py:

Python
import pytest
from pydantic import ValidationError

from computer import Computer


def test_computer_valid():
    laptop = Computer(brand="apple", ram_gb=16, hard_drive_gb=512)
    assert laptop.brand == "apple"
    assert laptop.ram_gb == 16
    assert laptop.hard_drive_gb == 512


def test_computer_rejects_string_ram():
    with pytest.raises(ValidationError):
        Computer(brand="apple", ram_gb="thirty two", hard_drive_gb=512)


def test_computer_requires_brand():
    with pytest.raises(ValidationError):
        Computer(ram_gb=16, hard_drive_gb=512)

Success

When uv run pytest reports 3 passed, you've confirmed Computer accepts a valid spec, rejects a string in ram_gb, and refuses to construct without a brand.