OdeToCode IC Logo

What’s Wrong With This Code (#25)

Monday, May 3, 2010

The goal: create an extension method that will make it easy to create FormCollection objects. The method is a helper for unit testing ASP.NET MVC code.

public static FormCollection ToFormCollection(this object data)
{
    var namesAndValues =
        data.GetType()
            .GetProperties()
            .WhereValueIsNotDefaultValue(data)
            .ToNameValueCollection(data);
    
    return new FormCollection(namesAndValues);
}

The extension method itself relies on a couple private extension methods:

static IEnumerable<PropertyInfo> WhereValueIsNotDefaultValue(
    this IEnumerable<PropertyInfo> properties, object source)
{
    foreach(var p in properties)
    {
        var value = p.GetValue(source, null);
        if(value != (p.PropertyType.IsValueType ? 
                     Activator.CreateInstance(p.PropertyType)
                     : null))
        {
            yield return p;
        }
    }
}

static NameValueCollection ToNameValueCollection(
    this IEnumerable<PropertyInfo> properties, object source)
{
    return properties.Aggregate(
            new NameValueCollection(),
            (nvc,pi) =>
                {
                    var value = pi.GetValue(source, null);
                    nvc.Add(pi.Name, value.ToString());
                    return nvc;
                });
}

The code mostly works. This test passes:

public void should_copy_property_values()
{
    var expectedName = "Scott";
    var expectedHireDate = new DateTime(2010, 1, 1);

    var data = new Employee
    {
        Name = expectedName,
        HireDate = expectedHireDate
    }.ToFormCollection();

    Assert.IsTrue(data["Name"] == expectedName);
    Assert.IsTrue(data["HireDate"] == expectedHireDate.ToString());
}

However, the helper should not put entries in the FormCollection for properties holding a default value, and this test fails:

public void should_not_copy_default_values()
{
    var data = new Employee
    {
        Name = "",
        HireDate = DateTime.Now
    }.ToFormCollection();

    Assert.IsNull(data["Tags"]);
    Assert.IsNull(data["Id"]);
}

Employee is defined as:

public class Employee
{
    public int Id { get; set; }
    public string Name { get; set; }
    public DateTime HireDate { get; set; }
    public string Tags { get; set; }
}

Hint: the first assert passes – it’s the assert on data[“Id”] that fails.