DEV Community

Cover image for From TDD to DDD: Building a .NET Core Web API - Part 5
LUCIANO DE SOUSA PEREIRA
LUCIANO DE SOUSA PEREIRA

Posted on

From TDD to DDD: Building a .NET Core Web API - Part 5

The complete project can be found here: https://github.com/lucianopereira86/CRUD-NetCore-TDD

Technologies

Topics

Post User • Theory

Red Step • Name

The Theory must be used to test the INVALID values from an entity. Some VALID values might be used as well but only to confirm that the test works perfectly for what it was designed for.

Let's start testing the possible values for the User's "Name" attribute.

Inside the "PostUserTest" class write the following code:

#region THEORY
[Theory]
[InlineData(null)]
[InlineData("")]
[InlineData("ABCDEFGHIJKLMNOPQRSTUVWXYZ")]
[InlineData("LUCIANO PEREIRA")]
public void Theory_PostUser_Name (string Name)
{
    var user = new User
    {
        Name = Name
    };
    Assert.Null(user.Name);
    Assert.Empty(user.Name);
    Assert.True(user.Name.Length > 20);
}

#endregion

The "Name" will be invalid if it be null, empty or exceeds 20 characters.
If you run the test, this will the result:

print09

The errors happened because all the conditions weren't respected at once. They should be tested separetely in different methods, but as the User class has many attributes, it would consume lots of lines of code and, most important, time.

To improve our development, we must use the "FluentValidation" package. It contains many functions that validate entire objects or attributes and return all the errors in a list.

Before doing it, rename the "Theory_PostUser_Name" method to "Theory_PostUser_Name_NoValidation" and change the code a bit:

[Theory]
[InlineData(null)]
[InlineData("")]
[InlineData("ABCDEFGHIJKLMNOPQRSTUVWXYZ")]
[InlineData("LUCIANO PEREIRA")]
public void Theory_PostUser_Name_NoValidation (string Name)
{
    var user = new User
    {
        Name = Name
    };

    var val = new PostUserValidator().Validate(user);
    Assert.False(val.IsValid);
}

All the validations will be implemented inside the "PostUserValidator" class. If one condition be disrespected, than the "val.IsValid" attribute will be false. It's time to go to the Green step.

Green Step • Name

Inside the Infra project, install the "FluentValidation" package through Nuget, create a folder named "Validations" and add into it a file named "PostUserValidator.cs". It will be responsible for validating only the "User" class attributes required in the POST method.

using CRUD_NETCore_TDD.Infra.Models;
using FluentValidation;

namespace CRUD_NETCore_TDD.Infra.Validations
{
    public class PostUserValidator: AbstractValidator<User>
    {
        public PostUserValidator()
        {
            RuleFor(x => x.Name)
                .Cascade(CascadeMode.StopOnFirstFailure)
                .NotEmpty()
                .MaximumLength(20);
        }
    }
}

Inside this code, the "Name" attribute must respect some rules.
The "Cascade(CascadeMode.StopOnFirstFailure)" means that if an error be encountered, it should return immediately from the field validation.

"NotEmpty" means that it should not be null or empty.

"MaximumLength" means that it should not have more than 20 characters.

These are the same rules we have stablished before.

Now, go back to the "PostUserTest" class, copy the "Theory_PostUser_Name_NoValidation" method, comment the original, rename the copy to "Theory_PostUser_Name_Validation" and import the "PostUserValidator" class. The file will be able to compile.

Run the test again and this will be the result:

print10

ATTENTION
The Green tests should have full green results to prove that all the validations have run correctly. But as a practice of mine, I put at least one valid value to show a red result as a safety measure. In no other way this approach should be used because it violates the TDD principles of the Green step.

All the conditions have been met except for the one containing a valid value ("LUCIANO PEREIRA") which was to be expected. So we can comment its line for further tests.

Next

But there is still a problem... How to prove that an specific condition was met instead of another one?

We will solve this but adding error codes for each one. Time for refactoring...

Oldest comments (0)