Business rules can be complicated and hence it becomes immensely important to automate the testing of those rules. The library, Fluent Validation, provides APIs to support the automated testing of the rules. In this article, we are going to have a look at how to unit test the validation logic written using fluent validation.
Before we begin !
I have been writing about the fluent validation library and below is the list of all the articles that I have published. I would recommend going through this list before coming to the unit testing part.
- Adding Fluent Validation in ASP .NET Core Web APIs
- ASP .NET Core API – Placeholders in Fluent Validation Messages
- ASP .NET Core API – Fluent Validations – Customizing Messages
- ASP .NET Core API – Fluent Validations – Collections
- ASP .NET Core API – Fluent Validators and Dependency Injection
- ASP .NET Core API – Fluent Validations – Cascade Behaviour
- Demo – Inheritance Validation using Fluent Validations
- ASP .NET Core API – Conditions in Fluent Validations
- ASP .NET Core API – Fluent Validations Based On Database Data
- ASP .NET Core API – Fluent Validation RuleSets
- ASP .NET Core API – Fluent Validation – Include Validators
Scenario
We are going to use the same scenario that I discussed in one of the previous posts.
Let’s say we have an entity,
Student
. It has properties –Id
,Name
, andVersion
, here Id is basically to uniquely identify the record. For calling AddStudent endpoint, id should be always zero, but for calling UpdateStudent endpoint, id should not be zero.The
Article about RuleSetsVersion
property is supposed to be used in update endpoint. It is to avoid overwriting more recent version of the record. So, Version should be non-zero while calling update endpoint and it should be zero while calling Add endpoint.
Then in the previous article, we created 5 different validators for validating properties from Student
class. Then these validators were used to compose another 2 validators.
- StudentWithIdValidator, to check that
Id
should be greater than zero - StudentWithoutIdValidator, to check that
Id
should be equal to zero - StudentWithVersionValidator, to check that
Version
should be greater than zero - StudentWithoutVersionValidator, to check that
Version
should be equal to zero - StudentNameValidator, to apply validations to Name property
- AddStudentValidator, which contains validators combined for
AddStudent
endpoint. This validator should be triggered from the add endpoint. - UpdateStudentValidator, which contains validators combined together for
UpdateStudent
endpoint. This validator should be triggered from the update endpoint.
The code snippet given below shows the code for these validators.
Create Unit Test Project
Let’s create a xUnit test project in the same solution where the API project containing above mentioned validators is located. Once the test project is created, add reference of the API project in the test project.
FluentValidation.TestHelper
This is a namespace in the Fluent Validation library. This namespace provides a method TestValidate
(or TestValidateAsync
) method. This method should be used for testing purposes. This method returns a result, TestValidationResult
, on which we can perform assertions.
There are two important assertion methods:
ShouldHaveValidationErrorFor
ShouldNotHaveValidationErrorFor
These methods take expressions as input, where we can specify the property, for which the validation has failed (or passed).
There are two more methods. We can use WithErrorMessage
to verify that the error message also matches. And we want to make sure that there are no other errors apart from ones specified in the assertion, then we can also use the method, Only
.
A Sample Unit Test
Let’s create a class student validator and let’s add tests for two scenarios.
- When Id is non-zero, version is zero and name has more than 10 characters, then the
AddStudentValidator
validation should fail for Id property. - When Id is non-zero, version is zero and name has more than 10 characters, then
UpdateStudentValidator
validation should fail for Version property.
As you can see in the code snippet given below, we have used assertion methods mentioned above to validate the validators. I hope this example should give fair idea about how the validators can be tested.
Points to Remember
If validators has asynchronous logic, then it is better to use TestValidateAsync
. Another thing is, when we are testing the types which are dependent on validators (e.g. controllers in our example), it is better to not mock the validator. We can mock the dependencies of validator if they have any.
I hope you find this information helpful. Let me know your thoughts.