Accessor? We don’ need no steeenking Accessor!

For whatever reason, developers don’t universally love the Visual-Studio-generated Accessor classes, as helpful as they may be.  They do solve a lot of problems when you’re writing unit tests, though.  It would be nice if you could achieve the same results without having to resort to an external class.  Now, through the magic of reflection, and extension methods, you can.

The idea started simply enough.  There are a number of places in my current project where developers (me included) have made methods public that really ought to be private or protected, just so that we can get to them for testing purposes.  Then, we’ve simply excluded those methods from the public interface that we are using to reference those objects.  We’re using StructureMap to handle dependency injection, so virtually every logic class, service, and controller is being used via an interface.  In our particular case, this works well, but it has still always bugged me that we’re changing the behavior of classes to facilitate testing, and then depending on a condition of our environment to make that "okay".  I wanted to keep those private members private, and just use reflection in the unit tests to get to the "naughty bits" that we shouldn’t be exposing, but I wanted to do it without the Accessor classes that so many people seem to despise.

What I wanted was to be able to get and set private fields and properties without having to go through a lot of extra Accessor class generation, and in a way that’s not going to clog up my unit tests with a lot of tedious reflection code. By writing a few extension methods against object (I know, I know), I can now write unit tests that look like this:

[TestMethod]
public void SetPrivatePropertyValue_sets_property_value()
{
    Target.SetPropertyValue("ReadOnlyProperty", "test");
    string value = Target.ReadOnlyProperty;
    Assert.AreEqual("test", value);
}

So far I’ve got extensions to get/set fields and properties, and to call non-public methods.  Unfortunately, I can’t seem to get away from having to name the properties as "magic strings".  These extensions aren’t meant for "real" day-to-day coding anyway, though.  They’re meant to be used from unit tests, and if you rename a property or method in your code, the unit tests should barf the next time you run them anyway, so any problems shouldn’t live long.  I’ve read some interesting information on Chad Meyers blog, but haven’t been able to make it work with my extension methods so far, or at least not without things getting messy.

Here is my current set of reflection extension methods:

public static class ReflectionHelper
{
    private const BindingFlags bindingFlags =
        BindingFlags.Instance | BindingFlags.NonPublic | BindingFlags.Public | BindingFlags.Static;

    #region Field operations

    public static FieldInfo GetFieldInfo<TClass>(string name)
    {
        FieldInfo fieldInfo = typeof(TClass).GetField(name, bindingFlags);
        return fieldInfo;
    }

    public static object GetFieldValue<TClass>(this TClass instance, string fieldName) where TClass : class
    {
        if (instance == null)
            throw new ArgumentNullException("instance");
        return GetFieldInfo<TClass>(fieldName).GetValue(instance);
    }

    public static void SetFieldValue<TClass>(this TClass instance, string fieldName, object value) where TClass : class
    {
        if (instance == null)
            throw new ArgumentNullException("instance");
        instance.GetType().GetField(fieldName, bindingFlags).SetValue(instance, value);
    }

    #endregion

    #region Property operations

    public static PropertyInfo GetPropertyInfo<TClass>(string name)
    {
        PropertyInfo result = typeof(TClass).GetProperty(name, bindingFlags);
        return result;
    }

    public static object GetPropertyValue<TClass>(this TClass instance, string propertyName) where TClass : class
    {
        if (instance == null)
            throw new ArgumentNullException("instance");
        return GetPropertyInfo<TClass>(propertyName).GetValue(instance, null);
    }

    public static object GetPropertyValue<TClass>(this TClass instance, string propertyName, object[] index) where TClass : class
    {
        if (instance == null)
            throw new ArgumentNullException("instance");
        return GetPropertyInfo<TClass>(propertyName).GetValue(instance, index);
    }

    public static void SetPropertyValue<TClass>(this TClass instance, string propertyName, object value) where TClass : class
    {
        if (instance == null)
            throw new ArgumentNullException("instance");
        instance.GetType().GetProperty(propertyName, bindingFlags).SetValue(instance, value, null);
    }

    public static void SetPropertyValue<TClass>(this TClass instance, string propertyName, object value, object[] index) where TClass : class
    {
        if (instance == null)
            throw new ArgumentNullException("instance");
        instance.GetType().GetProperty(propertyName, bindingFlags).SetValue(instance, value, index);
    }

    #endregion

    #region Method operations

    public static MethodInfo GetMethodInfo<TClass>(string methodName)
    {
        MethodInfo result = typeof(TClass).GetMethod(methodName, bindingFlags);
        return result;
    }

    public static object CallMethod<TClass>(this TClass instance, string methodName) where TClass : class
    {
        if (instance == null)
            throw new ArgumentNullException("instance");
        return instance.GetType().GetMethod(methodName, bindingFlags).Invoke(instance, null);
    }

    public static object CallMethod<TClass>(this TClass instance, string methodName, params object[] parameters) where TClass : class
    {
        if(instance==null)
            throw new ArgumentNullException("instance");
        return instance.GetType().GetMethod(methodName, bindingFlags).Invoke(instance, parameters);
    }

    #endregion
}

I’ll probably end up adding more later, but this is fine for my current needs.

About these ads
This entry was posted in Computers and Internet. Bookmark the permalink.

2 Responses to Accessor? We don’ need no steeenking Accessor!

  1. Derek says:

    Thanks! I was just looking at another post that had similar code (http://http://www.codeproject.com/KB/cs/testnonpublicmembers.aspx) when I came across yours. The other post doesn\’t use extension methods. Yours looks cleaner.However, I\’m confused about why you\’re using "BindingFlags.Static" when it seems you only really need "BindingFlags.Instance" (as your extension methods all take an "instance" parameter). This also brings up the point of how to call static properties or methods. Is it possible to use extension methods for those too?Thanks again.

  2. Mel says:

    The BindingFlags.Static is just force of habit, I suppose. I\’ve taken it out of my local copy since its not accomplishing anything.

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s