Click here to Skip to main content
65,938 articles
CodeProject is changing. Read more.
Articles / DevOps / unit-testing

Unit Testing Fluent Validations

5.00/5 (1 vote)
3 Sep 2021CPOL2 min read 22.6K  
Unit testing Fluent Validation methods for data validation
Unit testing is an important part of creating quality code. The unit test philosophy is to test the behavior of code to be sure that it's doing the work it was designed to do. A common problem that most applications face is validating input to be sure that the type and values are within the correct domain. Fluent Validations is a popular package for doing those data validations. This tip is how to unit test those data validation behaviors.

Introduction

Data validation is common in most applications. Fluent Validations are a very popular way to perform data validations data input.

This is a tip on how to unit test those validations to make sure they perform the validations that are expected.

Background

This tip assumes that there is a basic understanding of setting up the console application and the unit test project.

Here is a link to the FluentValidation page for further information and documentation on their libraries.

Here is the link to GitHub for the entire project code.

There are quite a few different test runners for unit testing, this tip uses MS Test as the test runner, the syntax may vary some if you use XUnit or NUnit or some other unit test runner.

Using the Code

This is a basic .NET Framework 4.8 console application to setup the code for the program.

The program does the following basic steps:

  • Creating a new Item object and setting the properties on that object.
  • Creating an instance of the ItemValidator class to validate the properties on the object instance.
  • Validating the values on the instance of the Item object.
  • Checking results and reporting errors back.

The item validator class inherits from the FluentValidations AbstractValidator base class:

  • The inherited class creates rules for each of the properties of the Item class.
  • Most of the validation rules are out of the box from Fluent Validation.
  • There is a custom validation for validation date values.
    C#
    using FluentValidation;
    using FluentValidation.Results;
    using System;
    using System.Collections.Generic;
    
    namespace UnitTestFluentValidations
    {
        class Program
        {
            static void Main(string[] args)
            {
                var item = new Item(12345, 23456789, new DateTime(1900, 01, 01));
                var itemValidator = new ItemValidator();
    
                ValidationResult results = itemValidator.Validate(item);
    
                bool success = results.IsValid;
                IList<validationfailure> failures = results.Errors;
    
                Console.WriteLine(failures);
            }
        }
    
        public class Item
        {
            public Item()
            {
    
            }
    
            public Item(int clientId, long orgUid, DateTime checkDate)
            {
                _clientId = clientId;
                _orgUid = orgUid;
                _checkDate = checkDate;
            }
    
            private int _clientId { get; set; }
            private long _orgUid { get; set; }
            private DateTime _checkDate { get; set; }
    
            public int ClientId
            {
                get { return _clientId; }
                set { _clientId = value; }
            }
    
            public long OrgUid
            {
                get { return _orgUid; }
                set { _orgUid = value; }
            }
    
            public DateTime CheckDate
            {
                get { return _checkDate;}
                set { _checkDate = value; }
            }
        }
    
        public class ItemValidator : AbstractValidator<item>
        {
            public ItemValidator()
            {
                RuleFor(x => x.ClientId).GreaterThan(0).NotNull().WithMessage
                       ("ClientId cannot be null");
                RuleFor(x => x.OrgUid).NotNull().GreaterThan(0).
                        WithMessage("OrgUid cannot be null");
                RuleFor(x => x.CheckDate).Must(BeAValidDate).WithMessage
                       ("Check date must be a validate date");
            }
    
            private bool BeAValidDate(DateTime date)
            {
                return !date.Equals(default(DateTime));
            }
        }
    }

    This is the packages.config to use for the program:

    XML
    <!--?xml version="1.0" encoding="utf-8"?-->
    <packages>
      <package id="FluentValidation" targetframework="net48" version="9.5.1">
    </package></packages>
    

    This is the unit test code laying out the tests to verify that the validations that were created in the program perform as expected.

  • The unit tests are named to describe the property and the property validation they are testing.
    C#
    using FluentValidation.TestHelper;
    using Microsoft.VisualStudio.TestTools.UnitTesting;
    using System;
    using UnitTestFluentValidations;
    
    namespace FluentValidationTests
    {
        [TestClass]
        public class FluentValidationTests
        {
            private ItemValidator validator;
    
            [TestInitialize]
            public void TestInitialize()
            {
                validator = new ItemValidator();
            }
    
            [TestMethod]
            public void Validate_ClientId_Is_Null()
            {
                var item = new Item { OrgUid = 123456, CheckDate = new DateTime(2020,4,1)};
    
                var result = validator.TestValidate(item);
                result.ShouldHaveValidationErrorFor(x => x.ClientId);
            }
    
            [TestMethod]
            public void Validate_ClientId_Is_LessThanZero()
            {
                var item = new Item {ClientId = -1, OrgUid = 123456, 
                           CheckDate = new DateTime(2020, 4, 1) };
    
                var result = validator.TestValidate(item);
                result.ShouldHaveValidationErrorFor(x => x.ClientId);
            }
    
            [TestMethod]
            public void Validate_OrgUid_is_Null()
            {
                var item = new Item { ClientId = 12345, 
                                      CheckDate = new DateTime(2020, 4, 1) };
    
                var result = validator.TestValidate(item);
                result.ShouldHaveValidationErrorFor(x => x.OrgUid);
            }
    
            [TestMethod]
            public void Validate_OrgUid_is_LessThanZero()
            {
                var item = new Item { ClientId = 12345, OrgUid = -1000, 
                                      CheckDate = new DateTime(2020, 4, 1) };
    
                var result = validator.TestValidate(item);
                result.ShouldHaveValidationErrorFor(x => x.OrgUid);
            }
    
            [TestMethod]
            public void Validate_CheckDate_is_InValid()
            {
                var item = new Item { ClientId = 12345, OrgUid = 321654987, 
                                      CheckDate = default(DateTime) };
    
                var result = validator.TestValidate(item);
                result.ShouldHaveValidationErrorFor(x => x.CheckDate);
            }
        }
    }

This is the packages.config to use for the unit test project:

XML
<!--?xml version="1.0" encoding="utf-8"?-->
<packages>
  <package id="FluentValidation" targetframework="net48" version="9.5.1">
  <package id="MSTest.TestAdapter" targetframework="net48" version="2.1.1">
  <package id="MSTest.TestFramework" targetframework="net48" version="2.1.1">
</package></package></package></packages>

Points of Interest

Unit testing can be one of the most effective tools that a developer has to help prevent code bugs from escaping into production systems. While this isn't a definitive how to on unit testing, this should be able to help in setting up unit tests on a popular data validation library.

History

  • 3rd September, 2021: Initial version

License

This article, along with any associated source code and files, is licensed under The Code Project Open License (CPOL)