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.
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:
<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.
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:
<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