Overview of Property-Based Testing Traditional unit testing follows the **Arrange-Act-Assert** pattern. You pick a specific input, run your code, and check if the output matches your manual calculation. While effective, this approach is limited by your own imagination; you only test the edge cases you can think of. Hypothesis shifts this paradigm by testing properties rather than specific examples. Instead of asserting that `add(1, 2)` equals `3`, you assert that `add(a, b)` always equals `add(b, a)`. This allows the framework to generate hundreds of random inputs to try and break your logic, often finding bugs in corners of the code you never thought to check. Prerequisites To follow this guide, you should have a solid grasp of Python fundamentals, including decorators and basic data structures. Familiarity with pytest is recommended, as we will use it to execute our test suites. You should also understand the basics of unit testing and assertion logic. Key Libraries & Tools * **Hypothesis**: A powerful library for property-based testing that generates test data and simplifies failing cases. * **pytest**: The standard testing framework used to run and organize Python test scripts. * **Haskell QuickCheck**: The original functional programming tool that inspired the property-based testing movement. Code Walkthrough: Reversible Operations A classic use case for property testing is an encoder-decoder pair. If you convert a string to ASCII codes and back, you should always end up with the original string. ```python from hypothesis import given, example from hypothesis.strategies import text from my_code import to_ascii_codes, from_ascii_codes @given(text()) @example("") def test_decode_inverts_encode(test_string): assert from_ascii_codes(to_ascii_codes(test_string)) == test_string ``` In this snippet, `@given(text())` tells Hypothesis to generate various strings. The `@example("")` decorator ensures that the empty string—a common edge case—is always included in the test run. When you run this with pytest, the library generates a wide array of Unicode characters and lengths to verify the property holds true. Custom Strategies with Composite Sometimes, simple types like integers or strings aren't enough. You might need to generate complex objects, like a team of employees. Hypothesis provides the `@composite` decorator to build these custom data generators. ```python from hypothesis import strategies as st @st.composite def teams_strategy(draw): size = draw(st.integers(min_value=1, max_value=20)) return generate_random_team(size) @given(teams_strategy()) def test_team_has_ceo(team): assert Employee.CEO in team ``` The `draw` function allows you to pull values from other strategies (like `integers`) and pass them into your business logic to create valid test objects. This modularity keeps your test code clean and reusable. Syntax Notes Notice the use of **decorators** to inject data into test functions. Hypothesis intercepts these functions and calls them repeatedly. Another important feature is **shrinking**: when Hypothesis finds a failure, it doesn't just give you a massive, confusing input. It automatically attempts to find the smallest, simplest version of that input that still triggers the error, making debugging significantly easier. Practical Examples & Tips Property testing excels at verifying **data invariants** (e.g., a sorting function should never change the length of a list) and **stateful systems**. **Tips & Gotchas:** * **Limit your ranges**: Use `min_value` and `max_value` in strategies to avoid generating unrealistic data that might cause timeouts. * **Don't abandon unit tests**: Use property-based testing for logic and invariants, but keep traditional unit tests for specific regression bugs. * **Settings**: Use the `settings` decorator to control `max_examples` if your tests are running too slowly in CI environments.
QuickCheck
Products
Jun 2022 • 1 videos
High activity month for QuickCheck. ArjanCodes among the most active voices, with 1 videos across 1 sources.
Jun 2022
- Jun 24, 2022