0 Comments

With unit testing one of the precepts is to check one thing in your test, but when you have objects this is difficult to express, since you want to check that the your business process has updated all of the expected properties (positive testing) and also that has not modified things that are not expected to have changed (negative testing).

This becomes even more problematic when you have an object graph, e.g. Order –> OrderLine –> Product where you have to navigate the graph, and reporting accurately where any errors is also a major issue as you need to track property names, collection indexes, etc. etc.

I wrote something to help me a few years ago and due to the nagging nudging of a colleague (I’m looking at you Rob Bagby), I’ve finally reformed it into something that’s hopefully useable by everyone else, called NCheck.

What this allows you to do is compare objects and the library will do a property by property comparison for you, handling collection properties and other referenced entities. What is nice I think, is that you still have control over what properties are included in the comparison and there are helpers for breaking object cycles using the your domain’s concept of identity.

Here’s a short introduction, given a simple class

public class Simple
{
    public int Id { get; set; }

    public string Name { get; set; }

    public double Value { get; set; }
}

we would like to test what happens after running some business service over it, we can do this easily with an NUnit test


[TestFixture]
public class SimpleTest
{
    [Test]
    public void AlgoTest()
    {
        var algo = new ShinyBusinssService();
        var source = new Simple { Id = 1, Name = "A", Value = 1.0 } ;

        var candidate = algo.Run(source);
        Assert.AreEqual(2, candidate.Id, "Id differs");
        Assert.AreEqual("B", candidate.Name, "Name differs");
        Assert.AreEqual(1.2, candidate.Value, "Value differs");
    }
}

If we re-write this using NCheck we get

[TestFixture]
public class SimpleTest
{
    [Test]
    public void AlgoTest()
    {
        var checkerFactory = new CheckerFactory();

        var algo = new ShinyBusinessService();
        var source = new Simple { Id = 1, Name = "A", Value = 1.0 } ;

        var expected = new Simple { Id = 2, Name = "B", Value = 1.2 } ;

        var candidate = algo.Run(source);
        checkerFactory.Check(expected, candidate);
    }
}

Behind the scenes, the CheckFactory class has created a comparison checker (using reflection) to compare each property of the Simple class, but you can also explicitly create a Checker<T> class if you want complete control.

The library is available as a NuGet package and the source code is on GitHub

0 Comments

I was working on the design of a small site where we have public display of information in one format and a set of administrative screens that allow authorised users to create and edit the information.

“I know”, I thought, “this is a perfect case for MVC Areas” – and boy was I wrong!

I should have realised that the problems had started when all the routing started playing up..

  1. Route names must be unique across *all* areas
  2. If there are controller names in common in different areas, all routes need to be updated to bind the namespace of the controller you want!

Yuck! – did the MVC team think about the open-closed principle?

Then I realised I’d made a category error – mis-applying the area concept. I was making the same sort of error that people make when implementing SOA, breaking down services according to functional granularity rather than business function.

So, instead of having two Product controllers, one in the main area, the other in the admin area, I should have one Product controller, still securing the appropriate methods, and still using routes to present some of the views under ~/admin/product and some under ~/product.

Areas should be reserved for coherent chunks of business functionality e.g. Sales, Marketing etc and I’m still not particularly happy that if I have a concept in common e.g. Customer, then the routes are going to clash, but I’m glad I realised my error before I’d built even more in the wrong direction.

0 Comments

Sometimes doing very basic things can be quite tricky in new technologies; case in point presenting a DropDownList (a ComboBox to those, including me, with a VB background) using the new EditorForModel syntax in ASP.NET MVC 2.

I had a little class in my MVP framework called IdLabel which as the name implied just held an Id and a Label so that I wasn’t forced to have a separate display and edit model for every trivial case, so I wanted to do the same sort of thing when writing my shiny new MVC apps. There’s a class called SelectListItem which fits the bill, but unfortunately a decision late in the MVC 2 Beta meant that by default, the object display/edit templates will not process anything other than basic types.

There is a slight hacky workaround which is to implement your own TypeConverter to tell the framework that it can be turned into a string, but this is normally then coupled by using an Attribute at compile time – and we don’t own SelectListItem. Fortunately, there’s a way around this using a little bit of reflection

public static class TypeUtility
{
    /// <summary>
    /// Registers a type converter against a type we don't own
    /// </summary>
    /// <typeparam name="T"></typeparam>
    /// <typeparam name="TConverter"></typeparam>
    public static void RegisterTypeConverter()
        where TConverter : TypeConverter
    {
        var attr = new Attribute[1];

        var vConv = new TypeConverterAttribute(typeof(TConverter));
        attr[0] = vConv;
        TypeDescriptor.AddAttributes(typeof(T), attr);
    }
}

So now starting with our very simple domain model

public class Supplier
{
    public int Id { get; set; }
    public string Name { get; set; }
}

public class Product
{
    public int Id { get; set; }
    public string Name { get; set; }
    public Supplier Supplier { get; set; }
}

we can have an equally simple view model

public class ProductViewModel
{
    public int Id { get; set; }
    public string Name { get; set; }
    [DropDownList]
    public SelectListItem Supplier { get; set; }
}
Now the custom DropDownList attribute and associated classes are taken from Kazi's blog with the slight modification of adding a couple of constructor overloads so that the defaults work for SelectListItem.

Having done all that we can proceed to what the controller has to do, what’s shown below is a simplification of my current controller which I'll tell you about shortly.

public ProductController 
{
     ...
     public ActionResult Edit(int id)
     {
          var entity = repository.FindOne(id);
          var model = builder.Convert(entity);
          
          var types = repository.FindAll();
          ViewData["SupplierList"] = builder.Convert(types);

          return VIew(model);
     }
     ...
}

The builder abstracts all of the work mapping the domain and view models and is based on AutoMapper.

Then we come to the display template, idea here is to show the label rather than the idea – one extension I have in mind is to convert this to a URL to allow display/edit of the related data.

<%@ Control Language="C#" Inherits="System.Web.Mvc.ViewUserControl" %>
<script runat="server">
    string FormattedModelValue
    {
        get
        {
            // TODO: Might want to test for DropDownList attribute for other complex models.
            if (Model == null)
            {
                return null;
            }
            return Model is SelectListItem ? ((SelectListItem) Model).Text : Model.ToString();
        }        
    }
</script>
<%= Html.Encode(FormattedModelValue) %>

The next bit is the edit template. I’m using majic strings to couple the list definition of the DropDownList to avoid having to make the views strongly-typed on SelectLists; I view this as outside of the scope of the view model, though you may disagree. If there’s a need to alter this, the first parameter of the DropDownList attribute takes the ViewData key name.

<%@ Control Language="C#" Inherits="System.Web.Mvc.ViewUserControl" %>
<%@ Import Namespace="Hippo.Web.Mvc.Templating" %>
<script runat="server">
    DropDownListAttribute GetDropDownListAttribute()
    {
        var metaData = ViewData.ModelMetadata as FieldTemplateMetadata;

        return (metaData != null) ? metaData.Attributes.OfType<DropDownListAttribute>().SingleOrDefault() : null;
    }
    
    IEnumerable<SelectListItem> GetSelectList()
    {
        var metaData = ViewData.ModelMetadata as FieldTemplateMetadata;        
        if (metaData == null)
        {
            return null;
        }

        var attribute = metaData.Attributes.OfType<DropDownListAttribute>().SingleOrDefault();
        if (attribute == null)
        {
            return null;
        }

        var key = attribute.ViewDataKey ?? (metaData.PropertyName + "List");
        var selected = attribute.GetSelectedValue(Model);
        // This makes it work for both SelectList and any other enumerable
        var sl = new SelectList(ViewData[key] as IEnumerable, attribute.DataValueField, attribute.DataTextField, selected);

        return sl;
    }
</script>
<% var attribute = GetDropDownListAttribute();%>
<% if (attribute != null) {%>
    <%= Html.DropDownList(null, GetSelectList(), attribute.OptionLabel, attribute.HtmlAttributes) %>
<% }%>
<% else {%>
    <%= Html.DisplayForModel()%>
<% }%>

The non-obvious problem here is that due to a bug in MVC 2, the Html.DropDownList builder ignores any selected item that you pass it and also does not look at the model (the offending line appears to be line 188 in SelectExtensions). The trick I found (today) was to push the Model value into the ViewData against the PropertyName, it does pick that up and pushes it as the selected item – there won’t be anything else there as we have a strongly-typed view, apart from the SelectLists.

One other thing to note is that Kazi’s DropDownList attribute can be given a list of anything and you can specify the Value and Text properties to be pushed into the SelectList. Also, your model doesn’t have to use SelectListItem, but if you don’t be prepared to write a TypeConverter each time you want a DropDownList!