Course contentsShow
← C#
Lesson 1645 of 1,90815. Software Quality and TestingFree lesson

Property-based Testing with FsCheck

Generate random inputs to verify invariants in your business logic.

In this lesson, you will learn how to shift from testing specific scenarios to verifying universal invariants using property-based testing with FsCheck.

Moving Beyond Example-Based Testing

Traditional unit tests (Example-based testing) rely on you to manually choose "interesting" inputs, such as 0, null, or 100. The risk is that you might miss edge cases where your logic fails. Property-based testing flips this: instead of writing tests for specific inputs, you define the properties of your code that must hold true for any valid input.

FsCheck automatically generates hundreds of random test cases based on the types you define, attempting to "break" your code. If it finds an input that violates your property, it performs shrinking, where it automatically simplifies the input to the smallest possible case that still triggers the failure, making debugging significantly easier.

Example: Verifying an Invariant

Imagine you have a Calculator class with an Add method. A mathematical property of addition is the commutative law: a + b must always equal b + a.

Instead of writing Assert.AreEqual(4, calculator.Add(2, 2)), you define a property:

using FsCheck;

public void AdditionIsCommutative()
{
    // Define the property as a Func<int, int, bool>
    Prop.ForAll<int, int>((a, b) => 
        new Calculator().Add(a, b) == new Calculator().Add(b, a)
    ).QuickCheckThrowOnFailure();
}

When you run this test, FsCheck will inject a wide range of integers—including negatives, zero, and very large numbers—into your test. If your implementation has a bug that only occurs when adding specific large integers, FsCheck will find it, shrink the input, and report the specific values that caused the mismatch.

This technique is powerful for testing business logic, data transformation pipelines, and complex algorithms where manual test cases rarely cover the full state space.

Key Takeaway: Property-based testing ensures software reliability by defining universal invariants and letting a generator engine attempt to falsify them with a massive range of random inputs.