Back to KB
Difficulty
Intermediate
Read Time
6 min

A Practical Guide to Property-Based Testing in Python

By Codcompass Team··6 min read

A Practical Guide to Property-Based Testing in Python

Property-based testing helps you test behavior by describing rules your code should always obey, instead of hand-picking a few example inputs. It is especially useful for validating edge cases, input validation, parsing, transformations, and logic that can fail in surprising ways.

Why this approach matters

Traditional example-based tests are good when you already know the exact inputs and outputs you want to check. Property-based testing shifts the focus to invariants, such as “sorting should not change the items,” “round-tripping should preserve data,” or “parsing then serializing should not lose information.”

That makes it a strong fit for code with lots of possible inputs, because it can uncover cases you did not think to write manually. It also encourages you to define what must always be true, which usually leads to clearer requirements.

What you will build

In this tutorial, we will test a small Python module that normalizes email addresses and processes payment amounts. The examples will use Hypothesis, a property-based testing library that generates many inputs for you and shrinks failing cases to something minimal and readable.

Project setup

Install Hypothesis and pytest:

pip install hypothesis pytest

Enter fullscreen mode Exit fullscreen mode

Create a file named app.py:

def normalize_email(email: str) -> str:
    local, domain = email.strip().lower().split("@")
    return f"{local}@{domain}"

def apply_discount(amount_cents: int, percent: int) -> int:
    if amount_cents < 0:
        raise ValueError("amount_cents must be non-negative")
    if not 0 <= percent <= 100:
        raise ValueError("percent must be between 0 and 100")
    return amount_cents - (amount_cents * percent // 100)

Enter fullscreen mode Exit fullscreen mode

This code is intentionally simple, but it gives us two good targets: string normalization and arithmetic invariants. Property-based tests are particularly effective when you want to check whole ranges of values rather than one fixed input.

First property test

Create test_app.py:

from hypothesis import given, strategies as st
from app import normalize_email

@given(st.text(min_size=1), st.text(min_size=1))
def test_normalize_email_is_lowercase(local, domain):
    email = f"{local}@{domain}"
    normalized = normalize_email(email)
    assert normalized == no

🎉 Mid-Year Sale — Unlock Full Article

Base plan from just $4.99/mo or $49/yr

Sign in to read the full article and unlock all 635+ tutorials.

Sign In / Register — Start Free Trial

7-day free trial · Cancel anytime · 30-day money-back