Testing Immutable Data Structures
In this lesson, you will learn how to verify the integrity of C# record types and ensure that your functional transformation pipelines behave predictably without unintended side effects.
When working with immutable data structures, your testing strategy shifts from verifying state changes to verifying transformation results. Because a record cannot be modified after instantiation, you aren't testing setters; you are testing that a method correctly returns a new instance with the expected data, while leaving the original instance untouched.
Verifying Transformation Pipelines
Consider a transformation pipeline that calculates discounts on an order. With immutable records, you verify that the source object remains identical after the operation and that the output object reflects the correct calculations.
public record Order(decimal Total, decimal Discount);
[Fact]
public void ApplyDiscount_ReturnsNewOrder_WithCorrectCalculation()
{
// Arrange
var original = new Order(100m, 0m);
var service = new OrderProcessor();
// Act
var updated = service.ApplyDiscount(original, 0.10m);
// Assert
// 1. Verify the result is correct
Assert.Equal(90m, updated.Total);
// 2. Verify immutability (The original instance is untouched)
Assert.Equal(100m, original.Total);
// 3. Ensure they are distinct instances
Assert.NotSame(original, updated);
}
Why This Matters
When your logic relies on functional chains—where you map, filter, or reduce data—the most common bug isn't a corrupted object; it’s a "leaked mutation." If a method accidentally modifies a property of a record, it breaks the contract of immutability and introduces unpredictable behavior elsewhere in the system.
By testing that the original instance remains in its initial state, you gain a high degree of confidence that your domain model remains pure. Furthermore, when testing deep transformation pipelines, focus on asserting the equality of the final record state. Because records implement value-based equality, you can compare the entire object graph using Assert.Equal(expected, actual), which simplifies your assertions significantly compared to checking individual properties of mutable classes.
Key Takeaway: Testing immutability means asserting that transformations return a new, correct instance while proving that the original reference remains strictly unchanged.