How to Test Properties with Random Inputs in Python
Write a simple property-based test in Python using random string generation to verify that string invariants like reverse-twice identity and uppercase idempotence always hold.
Python code
29 linesimport random
import string
def generate_random_string(length: int) -> str:
"""Generate a random alphanumeric string of given length."""
chars = string.ascii_letters + string.digits
return "".join(random.choice(chars) for _ in range(length))
def reverse_twice_is_identity(s: str) -> bool:
"""Property: reversing a string twice returns the original."""
return s[::-1][::-1] == s
def uppercase_is_idempotent(s: str) -> bool:
"""Property: applying uppercase twice is the same as once."""
return s.upper().upper() == s.upper()
if __name__ == "__main__":
random.seed(42)
test_cases = [generate_random_string(random.randint(1, 20)) for _ in range(100)]
reverse_results = all(reverse_twice_is_identity(s) for s in test_cases)
uppercase_results = all(uppercase_is_idempotent(s) for s in test_cases)
print(f"Reverse-twice invariant holds: {reverse_results}")
print(f"Uppercase-idempotence invariant holds: {uppercase_results}")
Output
Reverse-twice invariant holds: True
Uppercase-idempotence invariant holds: True
How it works
This code uses random and string from the standard library to generate random alphanumeric strings of varying lengths. The reverse_twice_is_identity function checks that reversing a string twice returns the original, which is a fundamental invariant of string reversal. The uppercase_is_idempotent function verifies that applying upper() twice produces the same result as applying it once, ensuring idempotence. By running these checks across 100 random test cases with a seeded random number generator, the tests are reproducible. Using all() over the list of boolean results confirms that every generated case passes the invariant.
Common mistakes
- Forgetting to seed the random number generator, which makes the test non-reproducible
- Using a fixed-length string, which reduces coverage of edge cases like empty strings
- Testing only happy-path inputs instead of randomized or boundary conditions
- Assuming built-in methods are always idempotent without verifying with tests
Variations
- Use the `hypothesis` library's `@given` decorator to generate random strings automatically
- Add an empty string to the test cases to verify invariants hold for the boundary case
Real-world use cases
- Verifying data normalization functions always return consistent results regardless of input order or duplication.
- Testing serialization and deserialization round-trips in APIs to ensure no data corruption occurs.
- Automating regression checks for string formatting logic in logging or report generation systems.
Sponsored
More from Testing & modern typing
- Capture stdout and stderr with pytest capsys easy
- Characterization Test for Legacy Python Code medium
- Dataclass with Type Hints Fields in Python easy
- Dependency Injection in Python for Testability easy
- Design Data Helpers with Python TypedDict and Literal easy
- Fix and Test a Regression Bug in Python with Unit Tests easy
Keep learning
Related tutorials and quizzes for this topic.