Testing Basic ASP.NET MVC View Model Validation With Brevity

by Jon Davis 1. December 2010 21:55

(Note: “Brevity” is not a proper noun. LOL .. sounds like it would make a great library name though doesn’t it?)

I recently attended a Desert Code Camp session that introduced attendees to Rails (as in, Ruby on Rails). I’d gone through similar introductions before, but I needed a refresher and another perspective from another mouthpiece. I was reminded yet again that the bulk of the power of this thingamajig, Rails, is plain vanilla code generation. There is no shortage of code generators in the .NET world, and it is nothing new (tools like MyGenerationSoftware, CodeSmith, T4, et al, have been around for years) but the problem with code generators is that you have to go back to the code generator if you need to adapt and refactor as business requirements evolve and knowledge and understanding increase (or as sanity wanes while workarounds perpetuate, omg).

One feature of Rails stuck out at me again, however, and that is RoR’s approach to TDD in model validation. In the session, the speaker cobbled together a simple unit test, threw some one-liner validation rules into the model, “rebuilt” the app, and watched the red/green output spit out immediately upon build. Once the validation rules were properly applied to the model, this immediately showed up in the web view scaffolding. This is exactly the way all development should be—some Ruby enthusiasts believe that unit tests deprecate the applicability for strongly typed language compilation altogether, I think that’s ridiculous, but I love the idea of supplementing compilation with post-build unit tests as a standard practice, not to mention combining the fundamental object metadata with the behavior of core view functionality in any UI framework.

I looked again at ASP.NET MVC and, having done rather little with the supposedly great testability of the ASP.NET MVC platform, had to figure out how to apply what I had just seen in this RoR session to the ASP.NET MVC platform scenario. I already knew that ASP.NET MVC supports easy peasy validation metadata out of the box, using the System.ComponentModel.DataAnnotations namespace and the attributes therein to decorate the properties of my view models. Notice the [Required] and [RegularExpression] attributes in the stupid sample below:

using System.ComponentModel.DataAnnotations;

namespace MvcApplication2.Models
{
    public class Fiz
    {
        [Required]
        public string Name { get; set; }

        [Required]
        [RegularExpression(".+@..+")]
        public string Email { get; set; }
    }
}
The problem, however, was that I was still green on how to actually validate that applying the ASP.NET MVC built-in validation functionality to an instance of this model actually works in a non-UI unit test. For example, let’s say I had not yet added the [Required] attributes in the above code yet, and, using red/green TDD coding practices, I first create a unit test and execute this test (seeing red, or “Fail”) before applying the validation rule. What would such a unit test look like?

That is actually the main purpose of this blog post, if for no other reason than to have it for my own reference. ;)

The first example I came across explained that one would need to actually have the controller for this model in place, and an appropriate action, such as Create(), to capture the model. This controller would then be able to expose the ModelState property which would be able to return a property indicating IsValid.

// unit test code, but don't use this
[TestMethod]
public void TestMyModel() {
    var fizController = new FizController();
    var unusedActionResult = fizController.Create(fiz);
    Assert.IsFalse(fizController.ModelState.IsValid); // doesn't even work anyway without bringing out the Validator
}
The author of this example continued on to explain how to invoke the validator to fill in the missing functionality.

// My own flavor, does not require explicit controller.
// Still, don't use. Not where we want to be yet.

private class ValidationController<T> : Controller
{
    public T Model { get; set; }
}

private Controller BindModelValidation<T>(T model)
{
    var controller = new ValidationController<T>();
    var validationContext = new ValidationContext(model, null, null);
    var validationResults = new List<ValidationResult>();
    Validator.TryValidateObject(model, validationContext, validationResults);
    foreach (var validationResult in validationResults)
    {
        controller.ModelState.AddModelError(validationResult.MemberNames.First(), validationResult.ErrorMessage);
    }
    controller.Model = model;
    return controller;
}

[TestMethod]
public void TestMyModel() {
    var fiz = new Fiz();
    var validationController = BindModelValidation(fiz);
    Assert.IsFalse(validationController.ModelState.IsValid);
}
And that’s fine, except for the fact that the separation of concerns (model vs. controller) is instantly lost; except where custom validation behavior is declared on an overriding controller there’s really no reason to even look at controller logic here. What we want to test is the model’s validation metadata and behavior in the context of ASP.NET MVC’s existing plumbing, nothing more, since we already trust that Microsoft already QA’d ASP.NET MVC and its controller and validator classes.

Fortunately, inspecting this code it seems Microsoft did provide a Validator class with static methods exposed to validate a model based on its metadata. You don’t need to involve a controller at all. If you look closely, Validator.TryValidateObject(..), while being stupid in that you have to pass in a context object (even if you’re gonna instantiate a new throwaway one anyway), will return a list of validation failures, or ValidationResults. So why don’t we just validate the validation by validating that the count of validation failures is not zero?

// Final code, I'm happy with this. Just need to move the
// helper method ValidateModel() to a base class or
// somewhere else that's clean and out of the way and
// won't be copied/pasted.
[TestMethod]
public void EmailRequired()
{
    var fiz = new Fiz 
        {
            Name = "asdf",
            Email = null
        };
    Assert.IsTrue(ValidateModel(fiz).Count > 0);
}

private IList<ValidationResult> ValidateModel(object model)
{
    var validationResults = new List<ValidationResult>();
    var ctx = new ValidationContext(model, null, null);
    Validator.TryValidateObject(model, ctx, validationResults, true);
    return validationResults;
}
Any reason why this won’t work? Any reason why Microsoft made brevity for validating model validation such an effort to be had?

Currently rated 4.0 by 1 people

  • Currently 4/5 Stars.
  • 1
  • 2
  • 3
  • 4
  • 5

Tags:

Comments

Add comment


(Will show your Gravatar icon)  

  Country flag

biuquote
  • Comment
  • Preview
Loading




 

Powered by BlogEngine.NET 1.4.5.0
Theme by Mads Kristensen

About the author

Jon Davis (aka "stimpy77") has been a programmer, developer, and consultant for web and Windows software solutions professionally since 1997, with experience ranging from OS and hardware support to DHTML programming to IIS/ASP web apps to Java network programming to Visual Basic applications to C# desktop apps.
 
Software in all forms is also his sole hobby, whether playing PC games or tinkering with programming them. "I was playing Defender on the Commodore 64," he reminisces, "when I decided at the age of 12 or so that I want to be a computer programmer when I grow up."

Jon was previously employed as a senior .NET developer at a very well-known Internet services company whom you're more likely than not to have directly done business with. However, this blog and all of jondavis.net have no affiliation with, and are not representative of, his former employer in any way.

Contact Me 


Tag cloud

Calendar

<<  September 2014  >>
MoTuWeThFrSaSu
25262728293031
1234567
891011121314
15161718192021
22232425262728
293012345

View posts in large calendar