In this post, I describe how to create class instances and access/set members from other assemblies when their access levels are set to internal or private.
When unit testing, you often need to generate instances of your domain objects within your tests. Hopefully, these object implements an interface you can mock with Rhino.Mocks (or your preferred mocking tool), or if not, provide virtual members that can be overridden in a derived class by a mocking tool. But sometimes, you run into a nasty class that makes your life difficult. I ran into this recently with the MS Dynamics 2011 SDK, where the Entity class, which drives most of the domain objects in the SDK, does not provide an interface, has internal-only setters on it's properties and has an internal-only constructor. And since Rhino.Mocks can't/won't mock non-virtual members, setting values to what you need for your tests is impossible without some work.
Luckily, you can get around internal and private access restrictions using reflection.
Note: I'll post the full set of classes, including some unit tests, at the end of this post, so in my examples here, I'm not going to include the using statements, etc.
Update 5/2/2012: Added Non-Public Fields section; Added base class recursive lookup for private fields/properties.
Non-Public Constructors:
So, let's start with the constructor, since you'll need an object to work with. The issues to deal with here is finding the right constructor based on the parameter list, so I'll use the Type.GetConstructor method.
public static T CreateInstance<T>(params object[] args)
{
Type typeToCreate = typeof(T);
Type[] parameterTypes = args.Select(arg => arg.GetType()).ToArray();
// Use reflection to get the ConstructorInfo object that matches our parameters
// even if it's not public.
ConstructorInfo constructorInfoObj = typeToCreate.GetConstructor(
BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic,
null, parameterTypes, null);
return (T) constructorInfoObj.Invoke(args);
}
To use it, you need only make a call like this:
var entity = MockingHelper.CreateInstance<ClassWithPrivateConstructors>(param1, param2);
Non-Public Properties:
Ok, now you want to set the value of a property where the setter is non-public. For this, I have a helper method that takes in the name of the property you want to set as a string, however I highly suggest you use the technique I suggested in my previous post for using reflection to get that string, thus limiting your risk of renaming the actual property and breaking your unit tests. This won't work, however, if the member you're trying to reach isn't visible to you, so you may have to resort to string-based names. (see unit tests at end of this post for full examples)
Note that if a member is private and is defined in a base class (ie: not the Type you're actually working with, but one of it's parent types), then you have to recursively walk the type hierarchy until you find what you're looking for.
public static void SetPropertyValue(object target, string memberName, object newValue)
{
PropertyInfo prop = GetPropertyReference(target.GetType(), memberName);
prop.SetValue(target, newValue, null);
}
private static PropertyInfo GetPropertyReference(Type targetType, string memberName)
{
PropertyInfo propInfo = targetType.GetProperty(memberName,
BindingFlags.Public |
BindingFlags.NonPublic |
BindingFlags.Instance);
if (propInfo == null && targetType.BaseType != null)
{
//if the member isn't actually on the type we're working on, rather it's
//defined in a base class as private, it won't be returned in the above call,
//so we have to walk the type hierarchy until we find it.
// See: http://agsmith.wordpress.com/2007/12/13/where-are-my-fields/
return GetPropertyReference(targetType.BaseType, memberName);
}
return propInfo;
}
public static string GetPropertyName<T>(Expression<Func<T>> property)
{
LambdaExpression lambdaExpression = (LambdaExpression)property;
var memberExpression = lambdaExpression.Body as MemberExpression ??
((UnaryExpression)lambdaExpression.Body).Operand as MemberExpression;
return memberExpression.Member.Name;
}
To use it, you need only make a call like this:
var entity = new ClassWithPrivatePropertySetter();
var memberName = MockingHelper.GetPropertyName(() => entity.MyString);
MockingHelper.SetPropertyValue(entity, memberName, "New Value");
Non-Public Fields:
Similar to properties, but a different call off Type:
public static void SetFieldValue(object target, string fieldName, object newValue)
{
FieldInfo field = GetFieldReference(target.GetType(), fieldName);
field.SetValue(target, newValue);
}
private static FieldInfo GetFieldReference(Type targetType, string fieldName)
{
FieldInfo field = targetType.GetField(fieldName,
BindingFlags.Public |
BindingFlags.NonPublic |
BindingFlags.Instance);
if (field == null && targetType.BaseType != null)
{
//if the field isn't actually on the type we're working on, rather it's
//defined in a base class as private, it won't be returned in the above call,
//so we have to walk the type hierarchy until we find it.
// See: http://agsmith.wordpress.com/2007/12/13/where-are-my-fields/
return GetFieldReference(targetType.BaseType, fieldName);
}
return field;
}
To use it, you need only make a call like this:
var entity = new ClassWithPrivatePropertySetter();
MockingHelper.SetFieldValue(entity, "_myStringField", "New Value");
Putting It All Together:
This is all good and whatnot, but to really make this clean, I would suggest creating some extension classes, or at least a helper class that will make these easier to call for your domain object, if you're going to do any really extensive testing. For example, I created these extension methods to allow me to set the internal-only Attributes property on the Dynamics EntityMetadata object. These are pretty simple, but I have others that create the entity (which has an internal-only constructor) and set several properties all in one swoop.
public static void SetAttributesViaReflection( this EntityMetadata entityMetadata,
AttributeMetadata [] attributes)
{
SetPropertyValue(entityMetadata,
GetPropertyName(() => entityMetadata.Attributes ),
attributes);
}
public static void SetOneToManyRelationshipsViaReflection(
this EntityMetadata entityMetadata,
OneToManyRelationshipMetadata [] relationships)
{
SetPropertyValue(entityMetadata,
GetPropertyName(() => entityMetadata.OneToManyRelationships ),
relationships);
}
Good Luck and happy reflecting!
The Full Source Code:
using System;
using System.Linq;
using System.Linq.Expressions;
using System.Reflection;
namespace jwright.blog
{
internal static class MockingHelper
{
public static T CreateInstance<T>(params object[] args)
{
Type typeToCreate = typeof(T);
Type[] parameterTypes = args.Select(arg => arg.GetType()).ToArray();
// Use reflection to get the ConstructorInfo object that matches our parameters
// even if it's not public.
ConstructorInfo constructorInfoObj = typeToCreate.GetConstructor(
BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic,
null, parameterTypes, null);
return (T) constructorInfoObj.Invoke(args);
}
public static void SetPropertyValue(object target, string memberName, object newValue)
{
PropertyInfo prop = GetPropertyReference(target.GetType(), memberName);
prop.SetValue(target, newValue, null);
}
private static PropertyInfo GetPropertyReference(Type targetType, string memberName)
{
PropertyInfo propInfo = targetType.GetProperty(memberName,
BindingFlags.Public |
BindingFlags.NonPublic |
BindingFlags.Instance);
if (propInfo == null && targetType.BaseType != null)
{
//if the member isn't actually on the type we're working on, rather it's
//defined in a base class as private, it won't be returned in the above call,
//so we have to walk the type hierarchy until we find it.
// See: http://agsmith.wordpress.com/2007/12/13/where-are-my-fields/
return GetPropertyReference(targetType.BaseType, memberName);
}
return propInfo;
}
public static void SetFieldValue(object target, string fieldName, object newValue)
{
FieldInfo field = GetFieldReference(target.GetType(), fieldName);
field.SetValue(target, newValue);
}
private static FieldInfo GetFieldReference(Type targetType, string fieldName)
{
FieldInfo field = targetType.GetField(fieldName,
BindingFlags.Public |
BindingFlags.NonPublic |
BindingFlags.Instance);
if (field == null && targetType.BaseType != null)
{
//if the field isn't actually on the type we're working on, rather it's
//defined in a base class as private, it won't be returned in the above call,
//so we have to walk the type hierarchy until we find it.
// See: http://agsmith.wordpress.com/2007/12/13/where-are-my-fields/
return GetFieldReference(targetType.BaseType, fieldName);
}
return field;
}
public static string GetPropertyName<T>(Expression<Func<T>> property)
{
LambdaExpression lambdaExpression = (LambdaExpression)property;
var memberExpression = lambdaExpression.Body as MemberExpression ??
((UnaryExpression)lambdaExpression.Body).Operand as MemberExpression;
return memberExpression.Member.Name;
}
}
}
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
namespace jwright.blog
{
public class ClassWithPrivateConstructors : BaseClassWithPrivateMembers
{
private ClassWithPrivateConstructors()
{
MyString = "parameterless";
}
private ClassWithPrivateConstructors(string stringToUse)
{
MyString = stringToUse;
}
public string MyString { get; set; }
}
public class ClassWithPrivatePropertySetter : BaseClassWithPrivateMembers
{
public ClassWithPrivatePropertySetter()
{
MyStringProperty = "parameterless";
}
public string MyStringProperty { get; private set; }
private string _myStringField;
public string MyStringFieldWrapper { get { return _myStringField; } }
}
public abstract class BaseClassWithPrivateMembers
{
private string _privateStringField;
public string PrivateStringFieldWrapper { get { return _privateStringField; } }
private String PrivateStringProperty { get; set; }
public string PrivateStringPropertydWrapper { get { return PrivateStringProperty; } }
}
}
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using NUnit.Framework;
namespace jwright.blog
{
[TestFixture]
public class UnitTests
{
[Test]
public void VerifyPropertyNamesCorrectlyDetermined()
{
var entity = new ClassWithPrivatePropertySetter();
var memberName = MockingHelper.GetPropertyName(() => entity.MyString);
Assert.That(memberName, Is.EqualTo("MyString"));
}
[Test]
public void VerifyPropertyValueSet()
{
var entity = new ClassWithPrivatePropertySetter();
var memberName = MockingHelper.GetPropertyName(() => entity.MyString);
var newValue = "New Value";
MockingHelper.SetPropertyValue(entity, memberName, newValue);
Assert.That(entity.MyString, Is.EqualTo(newValue));
}
[Test]
public void VerifyPrivateBaseClassPropertyValueSet()
{
var entity = new ClassWithPrivatePropertySetter();
var memberName = "PrivateStringProperty";
var newValue = "New Value";
MockingHelper.SetPropertyValue(entity, memberName, newValue);
Assert.That(entity.PrivateStringPropertydWrapper, Is.EqualTo(newValue));
}
[Test]
public void VerifyFieldValueSet()
{
var entity = new ClassWithPrivatePropertySetter();
var memberName = "_myStringField";
var newValue = "New Value";
MockingHelper.SetFieldValue(entity, memberName, newValue);
Assert.That(entity.MyStringFieldWrapper, Is.EqualTo(newValue));
}
[Test]
public void VerifyPrivateBaseClassFieldValueSet()
{
var entity = new ClassWithPrivatePropertySetter();
var memberName = "_privateStringField";
var newValue = "New Value";
MockingHelper.SetFieldValue(entity, memberName, newValue);
Assert.That(entity.PrivateStringFieldWrapper, Is.EqualTo(newValue));
}
[Test]
public void CreateInstanceTest_NoParams()
{
var entity = MockingHelper.CreateInstance<ClassWithPrivateConstructors>();
Assert.That(entity.MyString, Is.EqualTo("parameterless"));
}
[Test]
public void CreateInstanceTest_WithParam()
{
var testString = "a different value";
var entity = MockingHelper
.CreateInstance<ClassWithPrivateConstructors>(testString);
Assert.That(entity.MyString, Is.EqualTo(testString));
}
}
}