Overview
Property-based testing (PBT) is an approach to designing tests, in which attention is shifted from checking specific inputs and expected outputs to whether the results satisfy more general properties of a system or so-called invariants.
Unlike with traditional example-based approach, where we explicitly state specific examples of input data and their expected results, the idea with PBT is to specify general properties that should hold for all valid input data. The data generator creates random input data, and the test checks that the properties are preserved for each generated instance.
This approach is so called because it focuses on those characteristics of the system that need to be fulfilled whatever the input. Such characteristics are immutable, meaning the system has unchanging properties regardless of what data it uses.
Originally intended for use by developers in unit tests, the PBT approach can also be applied at a higher level to design and execute system tests.
Why Use PBT?
Below are the main advantages and disadvantages of PBT to help you decide on when and how to use this approach effectively.
Advantages
- Broader Test Coverage – PBT generates a wide variety of test cases including edge cases and unexpected inputs, which increases the chance of discovering hard-to-find bugs. It ensures the system behaves correctly over a broader input range, instead of being tuned to work only on carefully constructed examples.
- Automation of Test Case Creatio – PBT saves time and reduces human effort in writing specific test cases because test inputs are automatically generated. This allows testers to focus on finding meaningful properties rather than drawing inputs manually.
- Better Test Qualit – PBT encourages thinking on invariants and critical properties of the system, resulting in a deeper understanding of its requirements and behavior. Properties abstract away implementation details, which leads to better long-term maintainability of the tests.
- Generic and Reusable – Properties are reusable across multiple versions of a system, unlike example-based tests that may become outdated.
Disadvantages
- Difficulty in Defining Properties – Defining effective properties requires a deep understanding of the system and may be challenging for even experienced testers who are new to this approach. Besides, in some systems, it may be hard to generalize properties that capture the expected behavior completely (e.g. systems with complex GUI interactions or subjective UX).
- Performance Concerns – Generating and testing thousands of inputs can take longer than example-based tests, especially if the system has significant computational overhead or the tests run at GUI level.
Real-World Examples
Since PBT is a vigorous testing technique that verifies the generic properties or behaviors of the system under test rather than checking specific examples, its features make it suitable to be applied to both APIs and full web applications, and have particular advantages in testing quality and coverage improvement.
When testing API endpoints, PBT helps clearly define the properties based on API design conventions to validate its robustness because these properties are universal and apply to all APIs even when the business logic rules are not available.
In contrast, API testing usually deals with structured data and well-defined contracts, web applications have to cope with a variety of user interactions, browser environments, and input variations. With PBT applied, we are able to reproduce such scenarios by generating meaningful combinations of inputs in order to highlight rendering issues, bugs related to input handling, and unpredicted behavior coming from end users. It makes the web applications perform reliably under real-life conditions.
Let’s explore a few practical scenarios and define properties for them.
API Testing
Scenario 1: POST Endpoint
Test a POST /users endpoint that creates a user.
Properties
- Required fields must be validated.
- A valid request returns a 201 Created status code.
- The created user can be fetched with the returned ID.
- The API should not allow duplicate entries if uniqueness constraints exist.
Scenario 2: GET Endpoint
Test a GET /users/{id} endpoint that retrieves a user by ID.
Properties
- The response contains only the fields allowed by the API schema.
- Fetching the same user multiple times returns the same result (idempotency).
- Fetching a non-existent user returns 404 Not Found status code.
Scenario 3: PUT/PATCH Endpoint
Test a PUT /users/{id} or PATCH /users/{id} endpoint for updating user data.
Properties
- A valid request returns 200 OK or 204 No Content status code.
- Only the updated fields are modified; others remain unchanged.
- Immutable fields (e.g., id, dateCreated) should not be updatable.
- The state after an update should match the result of a GET request for the same resource.
- Repeated updates with the same payload should have no additional side effects (idempotency).
- Updating a non-existent user returns 404 Not Found status code.
Scenario 4: DELETE Endpoint
Test a DELETE /users/{id} endpoint for deleting a user.
Properties
- Deleted user cannot be retrieved.
- Deleting the same user multiple times should have no additional effects after the first deletion (idempotency).
- Deleting a non-existent user returns 404 Not Found status code.
We can also use a scenario common for all endpoints:
Scenario 5: API Security and Other Limitations
Test API authorization and other constraints.
Properties
- Only authorized users can access protected endpoints.
- API calls with payloads exceeding a size limit should be rejected.
- API calls exceeding rate limits should be rejected with 429 Too Many Requests status code and Retry-After header.
- API endpoints that modify data should handle concurrent requests gracefully and respond with 409 Conflict status code if race conditions occur.
Banking Application
Scenario 1: Funds Transfer
Test a feature that allows a user to transfer money from one account to another.
With traditional example-based testing, one would write specific test cases like:
- Transfer $100 from source account to destination account, where source account has $500 (should pass).
- Transfer $0 from source account to destination account (should fail).
- Transfer a negative amount (should fail).
With PBT, instead of specifying concrete data inputs, we define properties that the system should always satisfy. Then, we use a random data generator or a property-based testing library to generate a wide range of input data automatically.
Properties
- The total balance across both accounts remains unchanged after a valid transfer.
- Transfers with negative or zero amounts are invalid.
- The transfer fails if the amount exceeds the source account balance
Scenario 2: ATM Withdrawal
Withdraw money from an account via an ATM.
Properties
- The balance must decrease by the withdrawn amount plus any fees.
- The withdrawal amount cannot exceed the daily limit or available account balance.
Scenario 3: Transactions and Statements
Generate a statement of transactions for a given period.
Properties
- The closing balance equals the opening balance plus all credits minus all debits.
- Transactions are sorted by date in descending order.
E-Commerce Platform
Scenario 1: Searching and Filtering
Search for products and filter product catalog by various criteria.
Properties
- Every product in the result contains the search query in its name, description, or tags.
- If a category filter is applied, the result products belong to the selected category.
- If a price range is applied, the result product prices are within the range.
- If a rating range is applied, the result product ratings are within the range.
- Each product in the result satisfies both the search query and all applied filters simultaneously.
- Results are sorted correctly according to the selected sort criteria and order.
- If pagination is used, the products returned on each page are unique.
- Across all pages, the total products equal the filtered search result set size.
- Repeating the same query and filter combination on the same dataset produces identical results.
Scenario 2: Checkout
Test the calculation of total price at checkout, including taxes, discounts, and shipping fees.
Properties
- The total price should always be non-negative.
- Applying a discount cannot increase the total price.
- Adding an item to the cart increases or maintains the total price.
- Removing an item decreases or maintains the total price.
- Changing currency during checkout should convert the total price correctly.
- Updating items in the cart during checkout should keep the total price consistent.
- Shipping fees should correctly calculate based on weight, size, or destination.
Social Media Platform
Scenario 1: Posting and Commenting
Test that users can post and comment on other users’ posts.
Properties
- Posts and comments must adhere to length restrictions.
- Comments should maintain a hierarchical structure (parent-child relationships) and be displayed in the correct order.
- Adding or removing reactions (likes, emojis) must keep the counts accurate.
- The number of likes or reactions on a post should always be a non-negative integer.
- A post deleted by a user should not be retrievable via API or GUI.
Scenario 2: Privacy and Security
Test privacy and security settings.
Properties
- Privacy settings (e.g., public, friends-only) must restrict access to posts as configured by the user.
- A user’s feed should only contain posts they are authorized to see (e.g., posts from followed users).
- Posts from blocked users should not appear in the feed.
PBT Tools
When adopting Property-Based Testing, selecting the right tool is crucial for maximizing its benefits. For .NET and C# developers, FsCheck is a widely used library that integrates seamlessly with NUnit and xUnit, enabling expressive and powerful test generation. Another strong option is CsCheck, designed for high-performance PBT in C#.
In the JavaScript and TypeScript ecosystem, fast-check is the most popular choice, offering robust generators and built-in Jest compatibility, making it easy to incorporate into web application testing. JSVerify is another alternative that provides a functional approach to defining properties.
Beyond JavaScript and .NET, other notable PBT tools making property-based testing accessible across a wide range of programming languages include Hypothesis for Python—which integrates seamlessly with pytes—and QuickCheck for Haskell—the original PBT library that inspired many others.
These tools help automate test case generation, uncover hidden edge cases, and improve test coverage, making them valuable additions to any modern testing strategy.
Conclusion
The examples we reviewed illustrate how PBT can be integrated into real-world scenarios, ensuring scalability and reliability in modern software systems. By adopting this approach, teams can enhance their testing strategies, ultimately delivering higher-quality software to users.


