Fuzzing with hypothesis
Fuzzing generates numerous random inputs with various values to find cases where behavior is unexpected. Unlike previous tests, fuzzing is automatic.
Automatic generation is advantageous, allowing thousands of combinations. Writing this manually would be tedious.
Fuzzing detects:
- π― Possible edge cases not considered.
- β‘ Performance issues. Some inputs may take too long.
- π Inconsistencies, known as invariants and idempotency. For example, sorting a
list
and re-sorting it should yield the same result. Reversing a list twice should return the initial list.
Here is how to use the hypothesis
package. First, install it.
pip install hypothesis
Generate an example of an int
, a random int
.
import hypothesis.strategies as st
print(st.integers().example())
# -14251
Add restrictions, such as between 0
and 100
.
print(st.integers(min_value=0, max_value=100).example())
# 95
Generate text in str
, including all kinds of letters.
import hypothesis.strategies as st
print(st.text().example())
# ΓΒͺΒͺ󫦬aûæ4c
For multiple examples:
for _ in range(5):
print(st.integers().example())
hypothesis
can generate almost anything:
integers
: Integers orint
.text
: Text orstr
.floats
: Float numbers.booleans
:bool
types.lists
: Lists orlist
.tuples
: Tuples ortuple
.dictionaries
: Dictionaries ordict
.sets
: Sets orset
.
Now let us focus on integrating hypothesis
with pytest
.
Define a function that reverses an str
, changing abc
to cba
.
def reverse(s: str) -> str:
return s[::-1]
A good fuzzing example is verifying that using reverse
twice returns the initial value.
The following test generates 10000
random str
examples, verifying the property.
from hypothesis import given, settings
from hypothesis import strategies
@settings(max_examples=10000)
@given(strategies.text())
def test_reverse(s):
assert reverse(reverse(s)) == s
Run it as follows, and it will show statistics of the tested examples. In this case, everything worked correctly.
pytest -v --hypothesis-show-statistics
# 10000 passing examples
# 0 failing examples
# 0 invalid examples
In summary:
- Always write tests, even for simple code. They help verify functionality and protect against future changes.
- Include tests with unexpected inputs to ensure code resilience.
- Use coverage metrics as a guide, but remember that 100% coverage does not guarantee bug-free code.