Property-Based Testing
- In Turkish
- Özellik Tabanlı Test
In short
Property-based testing checks that a rule holds for many automatically generated inputs, instead of only for a handful of examples written by hand.
What is property-based testing?
Property-based testing is a technique where you describe a property, a rule that should be true for every valid input, and a library generates hundreds of inputs to try to break it. Typical properties are that reversing a list twice gives back the original list, or that sorted output is in order and contains the same elements as the input. The approach began with QuickCheck for Haskell around 2000 and is now available for most languages.
You tell the framework what kind of inputs to generate, such as integers, strings, or lists of user records, and it runs the test many times with random data. Generators deliberately include tricky edge cases, like empty lists, zero, negative numbers, very long strings, and unusual Unicode characters. When a failure appears, the framework shrinks the input, simplifying it step by step to the smallest case that still fails, so you debug [0, -1] instead of a list with 200 elements.
It's like testing a lock by hiring someone to try thousands of odd-shaped keys and report the simplest one that opens it, instead of only trying the three keys you own. Property-based testing works especially well for parsers and serializers, where decoding an encoded value must return the original, and for sorting, math, data transformations, pure functions, and comparing an optimized implementation against a simple reference one.
Property-based testing is often confused with fuzz testing. Both generate inputs automatically, but fuzzing usually throws large volumes of random or malformed data at a program for hours to find crashes and security holes, while property-based testing runs quickly in the normal test suite and checks specific correctness rules. It complements example-based unit tests rather than replacing them, since clear examples are still the best documentation of expected behavior.
Key takeaways
- You state a rule that must hold for all valid inputs, not a single expected output.
- The framework generates many inputs, including tricky edge cases.
- Failing inputs are shrunk to the smallest example that still fails.
- Round-trip properties such as decode(encode(x)) == x are a classic starting point.
- It checks correctness rules, while fuzzing mainly hunts for crashes.
Example
import json
from hypothesis import given, strategies as st
# Property: turning a dict into JSON and back must give the same dict.
# The library generates hundreds of dicts, including empty and unusual ones,
# and shrinks any failing input to the smallest example.
@given(st.dictionaries(keys=st.text(), values=st.integers()))
def test_json_round_trip(data):
assert json.loads(json.dumps(data)) == dataReaders ask
What is a property in property-based testing?
A property is a statement that should be true for every valid input, such as the output of a sort having the same length as its input. The test checks that statement against many generated inputs.
What is shrinking?
Shrinking is the step where the framework takes a failing input and repeatedly simplifies it, for example by removing items or making numbers smaller, until it finds the minimal input that still fails. This makes the bug much easier to understand.
See also
- Unit TestTesting & Quality, p. 35A unit test is a small, automated check that verifies one function, method, or class behaves correctly in isolation from the rest of the program.
- Fuzz TestingTesting & Quality, p. 11Fuzz testing is an automated technique that feeds a program huge numbers of unexpected or malformed inputs to find crashes, hangs, and security vulnerabilities.
- Pure FunctionProgramming Fundamentals, p. 47A pure function always returns the same output for the same input and has no side effects, meaning it does not change anything outside itself.
- Mutation TestingTesting & Quality, p. 17Mutation testing measures the quality of a test suite by inserting small deliberate bugs into the code and checking whether the tests fail and catch each one.
- AssertionTesting & Quality, p. 3An assertion is a statement in code declaring that a condition must be true at that point, stopping the test or program with an error if it is false.
- SerializationBackend & APIs, p. 41Serialization is the process of converting in-memory data structures into a format such as JSON or bytes, so they can be stored or sent over a network.
Spotted a mistake or something missing on this page?Suggest an edit