ASP.NET MVC, TDD and Fluent Validation

Yesterday I wrote about ASP.NET MVC, TDD and AutoMapper, and how you can use them together in a DDD application. Today I thought I would follow up and explain how to apply these techniques to another important (but boring) part of any web application: user input validation.

To achieve this, we are using Fluent Validation, a validation framework that lets you easily set up validation rules using a fluent syntax:

public class UserRegistrationFormValidator : AbstractValidator<UserRegistrationForm>
{
    public UserRegistrationFormValidator()
    {
        RuleFor(f => f.Username).NotEmpty()
            .WithMessage("You must choose a username!");

        RuleFor(f => f.Email).EmailAddress()
            .When(f => !String.IsNullOrEmpty(f.Email))
            .WithMessage("This doesn't look like a valid e-mail address!");

        RuleFor(f => f.Url).MustSatisfy(new ValidWebsiteUrlSpecification())
            .When(f => !String.IsNullOrEmpty(f.Url))
            .WithMessage("This doesn't look like a valid URL!");
    }
}

If you think about it, validation and view model mapping have similar footprints in the application. They both:

  • Live in the application services layer
  • May invoke domain services
  • Use third-party libraries
  • Have standalone fluent configurations
  • Have standalone tests
  • Are injected into the application services

Let’s see how it all fits together starting at the outermost layer, the controller.

public class AccountController : Controller
{
    readonly IUserRegistrationService registrationService;
    readonly IFormsAuthentication formsAuth;

    ...

    [AcceptVerbs(HttpVerbs.Post)]
    public ActionResult Register(UserRegistrationForm user)
    {
        if (user == null)
            throw new ArgumentNullException("user");

        try
        {
            this.registrationService.RegisterNewUser(user);
            this.formsAuth.SignIn(user.Username, false);
            return RedirectToAction("Index", "Home");
        }
        catch (ValidationException e)
        {
            e.Result.AddToModelState(this.ModelState, "user");
            return View("Register", user);
        }
    }

    ...
}

As usual, the controller is pretty thin, delegating all responsibility (including performing any required validation) to an application service that handles new user registration. If validation fails, all our controller has to do is catch an exception and append the validation messages contained within to the model state to tell the user any mistakes they made.

The UserRegistrationForm validator is injected into the application service along with any others. Just like AutoMapper, we can now test both the controller, validator and application service separately.

public class UserRegistrationService : IUserRegistrationService
{
    readonly IUserRepository users;
    readonly IValidator<UserRegistrationForm> validator;

    ...

    public void RegisterNewUser(UserRegistrationForm form)
    {
        if (form == null)
            throw new ArgumentNullException("form");

        this.validator.ValidateAndThrow(form);

        User user = new UserBuilder()
            .WithUsername(form.Username)
            .WithAbout(form.About)
            .WithEmail(form.Email)
            .WithLocation(form.Location)
            .WithOpenId(form.OpenId)
            .WithUrl(form.Url);

        this.users.Save(user);
    }
}

Testing the user registration form validation rules

Fluent Validation has some nifty helper extensions that make unit testing a breeze:

[TestFixture]
public class When_validating_a_new_user_form
{
    IValidator<UserRegistrationForm> validator = new UserRegistrationFormValidator();

    [Test]
    public void The_username_cannot_be_empty()
    {
        validator.ShouldHaveValidationErrorFor(f => f.Username, "");
    }

    [Test]
    public void A_valid_email_address_must_be_provided()
    {
        validator.ShouldHaveValidationErrorFor(f => f.Email, "");
    }

    [Test]
    public void The_url_must_be_valid()
    {
        validator.ShouldNotHaveValidationErrorFor(f => f.Url, "http://foo.bar");
    }
}

You can even inject dependencies into the validator and mock them out for testing. For example, in this app the validator calls an IUsernameAvailabilityService to make sure the chosen username is still available.

Testing the user registration service

This validation code is now completely isolated, and we can mock out the entire thing when testing the application service:

[TestFixture]
public class When_registering_a_new_user
{
    IUserRegistrationService registrationService;
    Mock<IUserRepository> repository;
    Mock<IValidator<UserRegistrationForm>> validator;

    [Test, ExpectedException(typeof(ValidationException))]
    public void Should_throw_a_validation_exception_if_the_form_is_invalid()
    {
        validator.Setup(v => v.Validate(It.IsAny<UserRegistrationForm>()))
            .Returns(ObjectMother.GetFailingValidationResult());

        service.RegisterNewUser(ObjectMother.GetNewUserForm());
    }

    [Test]
    public void Should_add_the_new_user_to_the_repository()
    {
        var form = ObjectMother.GetNewUserForm();

        registrationService.RegisterNewUser(form);

        service.Verify(
            r => r.Save(It.Is<User>(u => u.Username.Equals(form.Username))));
    }
}

Testing the accounts controller

With validation out of the way, all we have to test on the controller is whether or not it appends the validation errors to the model state. Here are the fixtures for the success/failure scenarios:

[TestFixture]
public class When_successfully_registering_a_new_user : AccountControllerTestContext
{
    [SetUp]
    public override void SetUp()
    {
        ... 
        result = controller.Register(form);
    }

    [Test]
    public void Should_register_the_new_user()
    {
        registrationService.Verify(s => s.RegisterNewUser(form), Times.Exactly(1));
    }

    [Test]
    public void Should_sign_in()
    {
        formsAuth.Verify(a => a.SignIn(user.Username, false));
    }
}

[TestFixture]
public class When_registering_an_invalid_user :  AccountControllerTestContext
{
    [SetUp]
    public override void SetUp()
    {
        ...
        
        registrationService.Setup(s => s.RegisterNewUser(form)).Throws(
            new ValidationException(
                ObjectMother.GetFailingValidationResult()));
        
        result = controller.Register(form);
    }

    [Test]
    public void Should_not_sign_in()
    {
        formsAuth.Verify(a => a.SignIn(It.IsAny<string>(),
            It.IsAny<bool>()), Times.Never());
    }

    [Test]
    public void Should_redirect_back_to_the_register_view_with_the_form_contents()
    {
        result.AssertViewRendered().ForView("Register")
            .WithViewData<UserRegistrationForm>().ShouldEqual(form);
    }
}

This post has been a bit heavier on code than usual, but hopefully it is enough to get an idea of how easy it is to implement Fluent Validation in your ASP.NET MVC application.

August 19, 2009

19 Comments

Jeremy Skinner on August 20, 2009 at 4:27 am.

Hi Richard,

Nice post. I’ve added a link to it from my blog and the FluentValidation wiki.

Also, the link in the post to the FV test extensions doesn’t seem to work.

Cheers,

Jeremy

alex pedenko on August 20, 2009 at 6:22 am.

nice timing :) – we implemented something like that in bistro mvc recently too.

Richard on August 20, 2009 at 8:47 am.

Jeremy -

Whoops, fixed the link. Cheers!

Richard on August 20, 2009 at 10:49 am.

Also I should probably note that ValidationException and ValidateAndThrow() are not included with FluentValidation (in case you were wondering why you can’t find them). Here’s the code for them:

public class ValidationException : Exception
{
    public ValidationResult Result { get; protected set; }

    public ValidationException(ValidationResult result)
    {
        Result = result;
    }
}

public static void ValidateAndThrow<T>(this IValidator<T> validator, T instance)
{
    ValidationResult result = validator.Validate(instance);
    if (!result.IsValid)
        throw new ValidationException(result);
}

Jake Scott on August 20, 2009 at 11:09 am.

Cool to find other kiwis who are into this! Whats up with the Fluent interface for building a user seems like overkill and it would possibly allow the user object to be invalid before saving it to the repository? Does the repository validate, or at this point is it up to Nhibernate to kick in with mappings and constraints?

Thanks for the article
Jake

Richard on August 20, 2009 at 11:42 am.

Jake,

We should do a roll call for kiwi alt.netters sometime :)

My User class a a pretty long constructor that will probably grow in future. The fluent interface is just a builder that collects the params and then calls the User ctor all at once.

I blogged about it before here: http://richarddingwall.name/2009/06/01/fluent-builder-pattern-for-classes-with-long-ish-constructors/

Jake Scott on August 22, 2009 at 1:25 pm.

Yeah sounds good, it seems there is more happening in welly than auckland in terms of awesome .net development. I will be reading your blog so cheers for the links.

Have you checked out this blog => http://weblogs.asp.net/rashid/default.aspx
If your doing mvc I recommend using the telerik asset management stuff for js + css compression and combining.

Cheers
Jake

Jake Scott on August 22, 2009 at 1:42 pm.

Haha just reading your couchdb article and I see Kazi reads your blog haha!

cowgaR on August 23, 2009 at 10:04 am.

good work Richard!
I’m in minority using FluentValidation, but I prefer it by all means!

Too bad all the other frameworks, even Microsoft (in upcoming MVC v2) is using attribute-based validation, which is forcing me to use partial classes = no go.

Jake Scott on August 26, 2009 at 2:06 pm.

Also attribute based validation is horrible when you want to anything that is not constant based. For example a simple “Date cannot be in the past” is impossible to do with attributes.

dario-g on September 15, 2009 at 11:53 am.

It’s amazing. I’ve just implementing like this in my app last night :) wow, wow, wow :)

Difference is with creating domain model. I’m using AutoMaper instead of builders and form data (ViewModel) is passed to service via interface (IUserRegistrationForm instead of UserRegistrationForm).

Do you have any possibility to view my desktop? ;)

Richard on September 15, 2009 at 12:11 pm.

Dario – we have tried using automapper in the reverse direction (i.e. updating domain model from the view edit model) but found it doesn’t really work because the domain model doesn’t have setters.

dario-g on September 15, 2009 at 9:31 pm.

Your example is in this post exactly suits to use AutoMapper but when you don’t have setters AutoMapper is (almost) not usable.

AutoMapper in my project is something like utility, not required convention. It helps me with simple models. :)

PS
I’m glad to see, that my ideas are so good (I think ;)).

Wes on October 16, 2009 at 9:26 am.

Have you tried using interception/AOP for dealing with validation exceptions?

Richard on October 16, 2009 at 1:38 pm.

I haven’t yet, but it wouldn’t be too hard to set up an action filter to do it instead?

Martin Aatmaa on February 1, 2010 at 11:23 am.

I agree with the overall flow, but I’m not convinced whether your ViewModels (UserRegistrationForm) should be in the ApplicationServices (UserRegistrationService) layer.

What if your ApplicationServices layer is shared by more than one client? For example, in addition to your Web MVC client, you want to add a Win console app. If this were the case, you ApplicationServices layer would have to know about ViewModels from both client apps, which seems smelly to me.

What do you think?

In any case, thank you for the informative post!

Richard on February 1, 2010 at 12:48 pm.

Martin – good question. In that case you would put a formal API in place that returns API-specific DTOs (effectively the same as a view model, without any specific UI in mind). For example, Twitter’s “get user” method might return the user’s details combined with their last ten tweets.

Then each client queries/send commands to the API, and maps its responses to their own UI-specific view models.

There should hopefully be a blog post about this (API-driven applications) in the next month or three :)

Drew Freyling on January 27, 2012 at 3:29 pm.

You don’t consider throwing exceptions for validation a bit heavy handed? Considering exceptions should really only occur in “exceptional” circumstances and the cost associated with throwing and catching them?

Leave a Reply