How To Set Elements Of An Array Of A Private Type Using Visual Studio Shadows
Visual Studio uses Publicize to create accessors public for private members and types of a type.
But when you try to set elements of a private array of elements of a private type, things get complicated.
Imagine this hypothetic class to test:
public static class MyClass { private static readonly MyInnerClass[] myArray = new MyInnerClass[10]; public static bool IsEmpty() { foreach (var item in myArray) { if ((item != null) && (!string.IsNullOrEmpty(item.Field))) { return false; } } return true; } private class MyInnerClass { public string Field; } }
If I want to write a test for the case when the array has “non empty” entries, I need to setup the array first.
Using the accessors generated by Visual Studio, I would write something like this:
[TestClass()] public class MyClassTest { [TestMethod()] public void IsEmpty_NotEmpty_ReturnsFalse() { for (int i = 0; i < 10; i++) { MyClass_Accessor.myArray[i] = new MyClass_Accessor.MyInnerClass { Field = i.ToString() }; } bool expected = false; bool actual; actual = MyClass.IsEmpty(); Assert.AreEqual(expected, actual); } }
But the test will fail because, although the elements of the private array myArray can be read as MyClass_Accessor.MyInnerClass instances, they can’t be written as such.
To do so, the test would have to be written like this:
[TestClass()] public class MyClassTest { [TestMethod()] public void IsEmpty_NotEmpty_ReturnsFalse() { for (int i = 0; i < 10; i++) { MyClass_Accessor.ShadowedType.SetStaticArrayElement("myArray", new MyClass_Accessor.MyInnerClass { Field = i.ToString() }.Target, i); } bool expected = false; bool actual; actual = MyClass.IsEmpty(); Assert.AreEqual(expected, actual); } }
But, this way, we loose all the strong typing of the accessors because we need to write the name of the array field.
Because the accessor for the field is a property, we could write a set of extension methods that take care of getting the field name for us. Something like this:
public static class PrivateypeExtensions { public static void SetStaticArrayElement<T>(this PrivateType self, Expression<Func<T[]>> expression, T value, params int[] indices) { object elementValue = (value is BaseShadow) ? (value as BaseShadow).Target : value; self.SetStaticArrayElement( ((PropertyInfo)((MemberExpression)(expression.Body)).Member).Name, elementValue, indices); } public static void SetStaticArrayElement<T>(this PrivateType self, Expression<Func<T[]>> expression, BindingFlags invokeAttr, T value, params int[] indices) { object elementValue = (value is BaseShadow) ? (value as BaseShadow).Target : value; self.SetStaticArrayElement( ((PropertyInfo)((MemberExpression)(expression.Body)).Member).Name, invokeAttr, elementValue, indices); } }
Now, we can write the test like this:
[TestClass()] public class MyClassTest { [TestMethod()] public void IsEmpty_NotEmpty_ReturnsFalse() { for (int i = 0; i < 10; i++) { MyClass_Accessor.ShadowedType.SetStaticArrayElement(() => MyClass_Accessor.myArray, new MyClass_Accessor.MyInnerClass { Field = i.ToString() }, i); } bool expected = false; bool actual; actual = MyClass.IsEmpty(); Assert.AreEqual(expected, actual); } }
It’s not the same as the first form, but it’s strongly typed and we’ll get a compiler error instead of a test run error if we change the name of the myArray field.
You can find this and other tools on the PauloMorgado.TestTools on CodePlex.