all Technical posts

Practically Property-Based Testing your Strict Domain Model in F# & C#

Property-based testing has been around some time, just as has domain modeling. However, the two don't always find each other in practical examples and projects.

This post will include domain modeling and property-based testing, but is by no intent an introduction to one or the other.

Strict Domain Modeling

By domain modeling, I mean a data model that holds strict specifications that are validated when creating the model. The ‘strict’ part here is to emphasize the need for strong and hardened models. Domain models can be corrupted, for example, if they can be created without validation or if the validation is not representing the domain.

Property-based testing is an excellent tool to verify if functionality works as expected — covering all grounds and all possibilities. So, why not use that in the most important and critical part of the project?

This post will use the Covid situation as a way to describe some of the simplified restrictions in place by the Belgium government. When going to events, one needs a Corona pass for entrance. This pass can represent a valid vaccination, a test result, or a certificate of Covid recovery. Only adults require such a pass. Note that I used my own validation library, FPrimitive, to describe this model, Expecto to describe the tests, and FsCheck to describe the properties and generators.

Generating Strict Models

Before we jump into the testing part of this blog, we should talk about generators. As you can see, we can’t just generate a random integer for the guest’s age or use any time span for the recovery period. This model can only be created with valid inputs, so we need to provide those in our generators.

A good tip in doing this is to split your generation models, just like in the domain, so you can easily access generator combinators.

As you can see, the different ways of creating a guest are all present here. If you want to generate an adult guest with a Corona pass, use Gen.Guest.adult. If you want to generate a list of guests of any age and pass, use Gen.Guest.list, and so forth.

When the domain changes, it will impact these generators. The user-friendly specification validation error in our domain will be shown in our test output, if that happens. This makes it a very tester-friendly approach.

Testing Domain Specifications

Now that we have our domain and generators to generate a version of the domain, we can test our domain specifications in properties. These are very straightforward properties. The trick is to find the constants. Our guest’s age expects an integer between 1-120. What constants are there? Any number below 1 or 120 is wrong, any number between 1-120 is right. That’s how simple this is.

Testing Trust Boundaries

One of the most simple test properties to write when working with your domain is the Round-Trip Property. Usually, to interact with the outside world, you need a DTO model (Data Transfer Object). This model is made for serialization and is efficient in data transfer, and easy to create and batch, etc. This can be a totally different model to your domain. Internally, a mapping has to be made between the two. The mapping can be tested easily with this property.

Let’s introduce our DTO model and our mapping towards the domain:

What a Round-Trip Property means is that we test it, we can map between the DTO and the domain model and back. Hence, the name ‘Round-Trip’. This property is proof that we don’t lose any information in the mapping process.

Testing Domain Constants

Now that we have the general part out of the way, we can start with some more ‘advanced’ work. First, let’s define a concert which people can attend only if they can prove they are valid guests according to the Covid restrictions.

Put really simply: children are allowed in by default, and adults are only allowed in with a pass that says they’re vaccinated, have tested negative, or have recovered from Covid. In those cases, the concert will provide them with an Allow door.

Now you’ll see the true power of properties. The exact way of describing the model can be used to describe the properties. It’s that simple.

What these properties also verify is that all the attended guests are checked and are always the same (i.e: guest that shows up twice).

Conclusion

Easys. FsCheck has a way of describing properties while using the C# xUnit test framework. The way to write them is a bit different and more verbose (because it’s C#) but the same properties can be achieved.

In this post, we saw what the possibilities are for practically using Property-Based Testing in a Domain Model. The two should definitely be used in close quarters, as they both talk about restrictions, validations, specifications, and ways to enforce all of these. Don’t be shy — ask for using any property-based testing framework in your project. You’ll see that this is not only very practical, but improves the code base greatly.

Thanks for reading,
Stijn

Subscribe to our RSS feed

Hi there,
how can we help?

Got a project in mind?

Connect with us

Let's talk

Let's talk

Thanks, we'll be in touch soon!

Call us

Thanks, we've sent the link to your inbox

Invalid email address

Submit

Your download should start shortly!

Stay in Touch - Subscribe to Our Newsletter

Keep up to date with industry trends, events and the latest customer stories

Invalid email address

Submit

Great you’re on the list!