How to set a private lazy<T> with reflection for testing purposes in C#?

c#, lazy-evaluation, reflection

Solution

Try making `SetProperty` a generic method:

public static void SetProperty<T>(object instance, string properyName, T value)

This should let you capture the type of `value`. With `T` in place, you could construct `Lazy<T>` object in the regular C# syntax, rather than going through reflection:

...
Lazy<T> lazyValue = new Lazy<T>(() => value);
...

Now you can write the `lazyValue` into the property/field with the `setValue` call.

This should be sufficient for many, if not all, of your unit tests.

Problem

The problem description We have a pretty big system, that used to eager load data into properies with private setters. For using testing specific scenarios, I used to write data in those properties with private setters. However, because the system was getting slow, and was loading unnessesary things, we changed certain things to lazy loading, using the Lazy class. However, now I'm no longer able to write data into those properties, so a lot of unit tests won't run anymore. What we used to have The object to test: ``` public class ComplexClass { public DateTime Date { get; private set; } public ComplexClass() { // Sample data, eager loading data into variable Date = DateTime.Now; } public string GetDay() { if (Date.Day == 1 && Date.Month == 1) { return "New year!"; } return string.Empty; } } ``` How the tests look like: ``` [Test] public void TestNewyear() { var complexClass = new ComplexClass(); var newYear = new DateTime(2014, 1, 1); ReflectionHelper.SetProperty(complexClass, "Date", newYear); Assert.AreEqual("New year!", complexClass.GetDay()); } ``` The implementation of the ReflectionHelper used in above sample. ``` public static class ReflectionHelper { public static void SetProperty(object instance, string properyName, object value) { var type = instance.GetType(); var propertyInfo = type.GetProperty(properyName); propertyInfo.SetValue(instance, Convert.ChangeType(value, propertyInfo.PropertyType), null); } } ``` What we have now The object to test: ``` public class ComplexClass { private readonly Lazy<DateTime> _date; public DateTime Date { get { return _date.Value; } } public ComplexClass() { // Sample data, lazy loading data into variable _date = new Lazy<DateTime>(() => DateTime.Now); } public string GetDay() { if (Date.Day == 1 && Date.Month == 1) { return "New year!"; } return string.Empty; } } ``` Attempt to solve it Now keep in mind, this is only one sample. The changes to code from eager loading to lazy loading is changed on a lot of different places. Because we don't want to change the code for all tests, the best option seemed to be to change the middleman: the `ReflectionHelper` This is the current state of `ReflectionHelper` Btw, I would like to apologize in advance for this weird piece of code ``` public static class ReflectionHelper { public static void SetProperty(object instance, string properyName, object value) { var type = instance.GetType(); try { var propertyInfo = type.GetProperty(properyName); propertyInfo.SetValue(instance, Convert.ChangeType(value, propertyInfo.PropertyType), null); } catch (ArgumentException e) { if (e.Message == "Property set method not found.") { // it does not have a setter. Maybe it has a backing field var fieldName = PropertyToField(properyName); var field = type.GetField(fieldName, BindingFlags.NonPublic | BindingFlags.Instance); // Create a new lazy at runtime, of the type value.GetType(), for comparing reasons var lazyGeneric = typeof(Lazy<>); var lazyGenericOfType = lazyGeneric.MakeGenericType(value.GetType()); // If the field is indeed a lazy, we can attempt to set the lazy if (field.FieldType == lazyGenericOfType) { var lazyInstance = Activator.CreateInstance(lazyGenericOfType); var lazyValuefield = lazyGenericOfType.GetField("m_boxed", BindingFlags.NonPublic | BindingFlags.Instance); lazyValuefield.SetValue(lazyInstance, Convert.ChangeType(value, lazyValuefield.FieldType)); field.SetValue(instance, Convert.ChangeType(lazyInstance, lazyValuefield.FieldType)); } field.SetValue(instance, Convert.ChangeType(value, field.FieldType)); } } } private static string PropertyToField(string propertyName) { return "_" + Char.ToLowerInvariant(propertyName[0]) + propertyName.Substring(1); } } ``` The first problem I came across attempting to do this, is that I was unable to create a delegate at runtime of an unknown type, so I tried to get around that by trying to set the internal values of the `Lazy<T>` instead. After setting the internal values of the lazy, I could see it was indeed set. However the problem I ran into doing that, was that I found out the internal field of a `Lazy<T>` is not a `<T>`, but actually a `Lazy<T>.Boxed`. `Lazy<T>.Boxed` is an internal class of lazy, so I'd have to instantiate that somehow... I realized that maybe I'm approaching this problem from the wrong direction, since the solution is getting exponentially more complex, and I doubt many people will understand the weird metaprogramming of the 'ReflectionHelper'. What would be the best approach in solving this? Can I solve this in the `ReflectionHelper` or will I have to go through every unittest and modify those? Edit after getting the answer I got a answer from dasblinkenlight to make SetProperty generic. I changed to code, and this is the end result, in case someone else needs it The solution ``` public static class ReflectionHelper { public static void SetProperty<T>(object instance, string properyName, T value) { var type = instance.GetType(); var propertyInfo = type.GetProperty(properyName); var accessors = propertyInfo.GetAccessors(true); // There is a setter, lets use that if (accessors.Any(x => x.Name.StartsWith("set_"))) { propertyInfo.SetValue(instance, Convert.ChangeType(value, propertyInfo.PropertyType), null); } else { // Try to find the backing field var fieldName = PropertyToField(properyName); var fieldInfo = type.GetField(fieldName, BindingFlags.NonPublic | BindingFlags.Instance); // Cant find a field if (fieldInfo == null) { throw new ArgumentException("Cannot find anything to set."); } // Its a normal backing field if (fieldInfo.FieldType == typeof(T)) { throw new NotImplementedException(); } // if its a field of type lazy if (fieldInfo.FieldType == typeof(Lazy<T>)) { var lazyValue = new Lazy<T>(() => value); fieldInfo.SetValue(instance, lazyValue); } else { throw new NotImplementedException(); } } } private static string PropertyToField(string propertyName) { return "_" + Char.ToLowerInvariant(propertyName[0]) + propertyName.Substring(1); } } ``` Breaking changes of this Setting variables to null no longer work without explicitly giving it a type. ``` ReflectionHelper.SetProperty(instance, "parameter", null); ``` has to become ``` ReflectionHelper.SetProperty<object>(instance, "parameter", null); ```

Original source