loading...
Cover image for From TDD to DDD: Building a .NET Core Web API - Part 7

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

lucianopereira86 profile image LUCIANO DE SOUSA PEREIRA ・2 min read

.NETCore + TDD + xUnit (7 Part Series)

1) From TDD to DDD: Building a .NET Core Web API - Part 1 2) From TDD to DDD: Building a .NET Core Web API - Part 2 3 ... 5 3) From TDD to DDD: Building a .NET Core Web API - Part 3 4) From TDD to DDD: Building a .NET Core Web API - Part 4 5) From TDD to DDD: Building a .NET Core Web API - Part 5 6) From TDD to DDD: Building a .NET Core Web API - Part 6 7) From TDD to DDD: Building a .NET Core Web API - Part 7

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

Technologies

Post User • Theory

Red Step • Age

Let's do a Theory for the "Age" attribute.

[Theory]
[InlineData(0, 102)]
[InlineData(-1, 102)]
[InlineData(33, 102)]
public  void Theory_PostUser_Age(int Age, int ErrorCode)
{
    var user = new User
    {
        Age = Age
    };

    CheckError(new PostUserValidator(), ErrorCode, user);
}

If you run the tests, there will error for all the values because we have not implemented the validation for the age yet. It must be greater than zero.

Green Step • Age

Inside the PostUserValidators constructor, add the following lines:

public PostUserValidator()
{
    RuleFor(x => x.Name)
        .Cascade(CascadeMode.StopOnFirstFailure)
        .NotEmpty()
        .WithErrorCode("100")
        .MaximumLength(20)
        .WithErrorCode("101");

    RuleFor(x => x.Age)
        .Cascade(CascadeMode.StopOnFirstFailure)
        .GreaterThan(0)
        .WithErrorCode("102");
}

Run the tests again and this will be the result:

print12

Only the valid value (33) has not returned any error, so our tests are working correctly again!

Post User • Fact II

Refactor Step II

There is no need for another Theory for the "PostUserTest" but another Fact is required to validate the data. Rename the "Fact_PostUser" method to "Fact_PostUser_NoValidation" and create another one:

 [Fact]
public void Fact_PostUser()
{
    // EXAMPLE
    var user = new User(0, "LUCIANO PEREIRA", 33, true);

    var val = new PostUserValidator().Validate(user);

    // ASSERT
    Assert.True(val.IsValid);

    if (val.IsValid)
    {
        // REPOSITORY
        user = new UserRepository(ctx).Post(user);

        // ASSERT
        Assert.Equal(1, user.Id);
    }
}

This time we are using the "Assert" class to ensure that the validation will be true before accessing the repository. Run the test again.

print13

This must be your project so far:

print14

Coming soon...

The PUT method will be the next to receive tests, repository and validation.

.NETCore + TDD + xUnit (7 Part Series)

1) From TDD to DDD: Building a .NET Core Web API - Part 1 2) From TDD to DDD: Building a .NET Core Web API - Part 2 3 ... 5 3) From TDD to DDD: Building a .NET Core Web API - Part 3 4) From TDD to DDD: Building a .NET Core Web API - Part 4 5) From TDD to DDD: Building a .NET Core Web API - Part 5 6) From TDD to DDD: Building a .NET Core Web API - Part 6 7) From TDD to DDD: Building a .NET Core Web API - Part 7

Posted on Dec 16 '19 by:

lucianopereira86 profile

LUCIANO DE SOUSA PEREIRA

@lucianopereira86

Luciano de Sousa Pereira. Brazilian. Bacharel degree in Computer Engineering. Full Stack Developer with 5 years of experience in multiple frameworks and programming languages.

Discussion

markdown guide
 

I think I'm missing something? Shouldn't we want the green step to actually go "full green". Shouldn't there not be any failing tests? Earlier in part 5, you just kind of say to "comment out of the test", is this something you are planning on addressing later?

 

Thanks for the contact.
Probably you are talking about "Theory".
In fact, the way I work is to give multiple values to a "Theory" and expected at least one of them to be red.
Why?
Because this way I can make sure that the validation won't fail if a valid value be used.
If you do not agree with this practice, just comment the "InlineData" that would return a red test result.
Have I answered your question?

 

Yes, that is the part I was talking about. I don't understand why you would want a failing test case to "stick around". IMO, this would cause you to need to review the tests every time to confirm you didn't actually break something. Image having thousands and thousands of test cases with several developers. I feel you would need to consistently be checking which ones are failing to know if you broke something or if that is "one of the ones that supposed to be there".

Just my 2 cents

Unfortunately, I've had a bad experience in the past that made me paranoic about the possibility of a valid value being considered invalid. Since then, I've tested it as well in my Theories. I know I am breaking the a TDD rule, but it was only my vision as developer.
I will edit my posts to let very clear that this approach was my decision.
Thanks anyway.

What about making the test "invertible", by adding a bool parameter which will swap failure and success around if set to true? That way the test would still exist and be green

good idea.
I've made something similar but using enum.

to make it clear, have separate SUCCESS and FAIL test is a good thing, like:

  • expect that all users with names less than 20 chars to be accepted
  • expect all users with names more than 20 chars to be rejected (rejection is accepted so it's our GREEN indicator).

best to do one test per fact, e.g. test the 20 char limit, test if there's at least a special char in the password and have multiple values for that test that should pass.

same way give multiple values for the test where the failing condition is tested, e.g.:
fact_password_without_special_chars_should_be_rejected