Showing posts with label Testing. Show all posts
Showing posts with label Testing. Show all posts

Monday, 19 January 2009

Refactored: Fluent Test Data Builders Using Generic Extension Methods and Lambda Expressions

Working further with the Fluent Test Data Builder I was working on yesterday has led me to refactor what I had, and the result is, I think, even nicer!

I've added a new method SetValue, and changed the from getting the Member.Name to getting the PropertyInfo from the Member. The other significant change is that I'm now wrapping the SetLength and SetValue methods in try catch blocks so that I can intercept a TargetInvocationException and throw the InnerException in its place, this helps to ensure that when I use the ExpectedExceptionAttribute I am catching the exception I really expect. The drawback with this is the stack trace, but I'm content to live with that for now.

publicstatic T SetLength<T>(this T obj, Expression<Func<T, string>> expression, int length)
{
try
    {
        PropertyInfo member = getMemberExpressionMember(expression.Body);
        string currentValue = expression.Compile().Invoke(obj) ?? string.Empty;
        string newValue = currentValue.PadRight(length);

        member.SetValue(obj, newValue, null);

        return obj;
    }

    catch (TargetInvocationException tie)
    {
        if (tie.InnerException != null) throw tie.InnerException;
        throw;
    }
}
 
public static TObj SetValue<TObj, TVal>(this TObj obj, Expression<Func<TObj, TVal>> expression, TVal value)
{

    try
    {
        PropertyInfo member = getMemberExpressionMember(expression.Body);
        member.SetValue(obj, value, null);
        return obj;
    }
    catch (TargetInvocationException tie)
    {
        if (tie.InnerException != null) throw tie.InnerException;
        throw;
    }
}



So how am I making use of this fluent interface? Well the methods above are two of those I have written, and probably the most interesting, but I have others too. These fall into two main categories:



  1. setting default valid values;

  2. creating default valid objects.


For setting default valid values, these are done as ExtensionMethods on the type I'm interested in. For example:



        public static Extract WithDefaultTitle(this Extract extract)
        {
            extract.QualificationTitle = "Mr";
            extract;
        }


For creating default valid objects, this is also done with an ExtensionMethod on the type that I'm interested in. As my need for different default starting points increases then I can easily write more. Here's an example:


        public static Extract AsValid(this Extract extract)
        {
            return extract
                .WithDefaultCode()
                .WithDefaultTitle()
                .SetValue(e => e.Id, 111)
        }


With these methods available to me I can combine them with my SetValue and SetLength methods to then alter the state of any given property so that I can test the implications of the value set in a degree of isolation (at least consistency).  In particular I am using this to test that behaviour to validate my object works correctly given different combinations of property values on the object under test. Here's an example of this:


        [Test]
        [ExpectedException(typeof(InvalidOperationException), "An Id must be present.")]
        public void NullIdShouldThrowAnInvalidOperationExceptionOnValidate()
        {
            Extract extract = new Extract()
                .AsValid()
                .SetValue(e => e.Id, null);
            extract.Validate();
        }
 
        [Test]
        public void A_SurnameWithLessThan35CharactersShouldBePaddedTo35CharactersInFormattedProperties()
        {
            Extract extract = new Extract()
                .AsValid()
                .SetLength(e => e.Surname, 34);
                
            Assert.AreEqual(34, extract.Surname.Length);
            Assert.AreEqual(35, extract.FormattedProperties["Surname"].Length);
        }

Personally I'm happy with how this is progressing, but we'll see how it goes as I begin to push the current envelope a bit more.

Sunday, 18 January 2009

Fluent Test Data Builders Using Generic Extension Methods and Lambda Expressions

Whilst writing some extension methods this evening to make creating objects in unit tests simpler I found myself writing methods that looked sufficiently similar to each other that I felt the code lacked a little DRYness.

The code being written has to output a data extract in a fixed field length formatted text file. As such the length of the formatted fields is very important, so I had created some methods to initialise the properties of my object with given lengths so that I could ensure the desired behaviour during formatting (such as trimming, padding, exception throwing, etc...). These methods looked much like this:

public static DataExtract WithSetLengthSurname(this DataExtract dataExtract, int length) 
{ 
    dataExtract.Surname = string.Empty.PadRight(length); 
    return dataExtract; 
}
 
public static DataExtract WithSetLengthForenames(this DataExtract dataExtract, int length) 
{ 
    dataExtract.Forenames = string.Empty.PadRight(length); 
    return dataExtract; 
} 
 

As you can see these methods are very similar. What I wanted I felt was to take this fluent interface for creating my DataExtract object, but to make the whole thing more generic.


This is what I came up with:


public static T SetLength<T>(this T obj, Expression<Func<T,string>> expression, int length) 
{ 
    string memberName = getMemberExpressionMemberName(expression.Body); 
    string currentValue = expression.Compile().Invoke(obj) ?? string.Empty; 
    string newValue = currentValue.PadRight(length); 
    obj.GetType().GetProperty(memberName).SetValue(obj, newValue, null); 
    return obj; 
} 
 
private static string getMemberExpressionMemberName (Expression expression) 
{ 
    if (expression == null) return string.Empty; 
 
    switch (expression.NodeType) 
    { 
        case ExpressionType.MemberAccess: 
            { 
                MemberExpression memberExpression = (MemberExpression) expression; 
                return memberExpression.Member.Name; 
            } 
        default: 
            throw new InvalidOperationException(string.Format("The Expression Type was not expected: {0}", expression.NodeType)); 
    } 
 
}

With this code then I can now make a call like this:


DataExtract extract = new DataExtract().SetLength((e) => e.Forenames, 10);

Now it's not unreasonable to look at this and wonder what has been gained, after all I could have written this:


DataExtract extract = new DataExtract { Forenames = "".PadRight(10) };

I suppose for me it comes into its own when combined with other extension methods as a part of a wider ranging Fluent Interface based test object builder, like this:


CandidateExtract extract = new CandidateExtract() 
    .SetLength((e) => e.Surname, 36) 
    .WithValidQualificationCode() 
    .WithValidQualificationTitle() 
    .SetLength((e) => e.Forenames, 36);

As can be seen in the above some property specific methods still exist, because the behaviour that each implements is sufficiently unique, but where the behaviour is very similar (such as setting a string to a default length) this seems to me to provide a number of benefits:


  • the number of methods appearing in Intellisense drops making it easier to find what you want quickly
  • the number of builder methods to be written drops (in my case significantly)
  • by using a fluent interface readability is enhanced.

Sunday, 28 September 2008

Testing DateTime Type Properties for Value Equality in Integration Tests

I've just been writing some integration tests to ensure that some Repositories I'm writing work with NHibernate properly, basically ensuring that the mappings do what I want. The Repository classes in question use the latest release of NHibernate (2.0.0.GA) for their implementation and I'm using MbUnit for my integration tests.

To try and save on the number of asserts that I have to write I thought that I'd try to use the Assert.AreValueEqual method that MbUnit provides. This allows me to write this in my test:

foreach (PropertyInfo propertyInfo in typeof(Candidate).GetProperties())
{
Assert.AreValueEqual(propertyInfo,expected,actual, null);
}

The context for this test is that I'm persisting a copy of a new Candidate object to the database (using NHibernate, but not my repository) which exposes a property, RegistrationDate, of the type ?DateTime. When this property is cycled through to the Assert always fails. Initially this puzzled me as the two DateTime instances appeared to be the same. A quick look at the DateTime.Equals(DateTime) method in reflector soon revealed that when this operation is called it is the InternalTicks property that is the subject of the comparison:

return (this.InternalTicks == value.InternalTicks);

When I get the DateTime back from the database this is not as precise as it was originally in the world of the CLR. The expected (original) value is 633581595292333682 whilst the actual (retrieved) value is 633581595290000000. This isn't enough of a difference to be significant for my purposes, as the ToString() representations are Equal and I don't need a level of accuracy beyond days, let alone fractions of a second.

I figure my options here are:

  1. Don't use Assert.AreValueEqual and hand write each Assertion as an Assert.AreEqual instead;
  2. Store the DateTime in the database as the value of the InternalTicks rather than the DateTime value itself. The obvious problem I can see with this is that any extraction, such as to a reporting database, would become more problematic if this was done. I figure there are times when testing should inform design, but this is clearly not one of them!
  3. Isolate NullableDateTime type properties and deal with these differently.

By changing the code a little I am able to isolate DateTime properties and deal with these using the ToString() method like this:

Type NullableDateTimeType = Type.GetType("System.Nullable`1[[System.DateTime, mscorlib, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089]]");
foreach (PropertyInfo propertyInfo in typeof(Candidate).GetProperties())
{
if (propertyInfo.PropertyType == NullableDateTimeType)
{
DateTime expectedDateTime = (DateTime)propertyInfo.GetValue(expected, null);
DateTime actualDateTime = (DateTime)propertyInfo.GetValue(expected, null);
Assert.AreEqual(expectedDateTime.ToString(), actualDateTime.ToString(), "The expected RegistrationDate is not equal to the actual RegistrationDate.");
}
else
{
Assert.AreValueEqual(propertyInfo, expected, actual, null);
}
}

I'm not too sure how much coding I've saved myself with this, but I figure that as the number of properties involved increases it will reduce maintenance. If anyone out there has a better solution (doing this at gone 2 in the morning makes me think that there probably is a better solution) then please feel free to suggest it.