hits counter

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];
<SPAN style="COLOR: blue">public static bool </SPAN>IsEmpty()
{
    <SPAN style="COLOR: blue">foreach </SPAN>(<SPAN style="COLOR: blue">var </SPAN>item <SPAN style="COLOR: blue">in </SPAN>myArray)
    {
        <SPAN style="COLOR: blue">if </SPAN>((item != <SPAN style="COLOR: blue">null</SPAN>) &amp;&amp; (!<SPAN style="COLOR: blue">string</SPAN>.IsNullOrEmpty(item.Field)))
        {
            <SPAN style="COLOR: blue">return false</SPAN>;
        }
    }

    <SPAN style="COLOR: blue">return true</SPAN>;
}

<SPAN style="COLOR: blue">private class </SPAN><SPAN style="COLOR: #2b91af">MyInnerClass
</SPAN>{
    <SPAN style="COLOR: blue">public string </SPAN>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() };
        }
    <SPAN style="COLOR: blue">bool </SPAN>expected = <SPAN style="COLOR: blue">false</SPAN>;
    <SPAN style="COLOR: blue">bool </SPAN>actual;

    actual = <SPAN style="COLOR: #2b91af">MyClass</SPAN>.IsEmpty();

    <SPAN style="COLOR: #2b91af">Assert</SPAN>.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);
        }
    <SPAN style="COLOR: blue">bool </SPAN>expected = <SPAN style="COLOR: blue">false</SPAN>;
    <SPAN style="COLOR: blue">bool </SPAN>actual;

    actual = <SPAN style="COLOR: #2b91af">MyClass</SPAN>.IsEmpty();

    <SPAN style="COLOR: #2b91af">Assert</SPAN>.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(
        ((<SPAN style="COLOR: #2b91af">PropertyInfo</SPAN>)((<SPAN style="COLOR: #2b91af">MemberExpression</SPAN>)(expression.Body)).Member).Name,
        elementValue,
        indices);
}

<SPAN style="COLOR: blue">public static void </SPAN>SetStaticArrayElement&lt;T&gt;(<SPAN style="COLOR: blue">this </SPAN><SPAN style="COLOR: #2b91af">PrivateType </SPAN>self, <SPAN style="COLOR: #2b91af">Expression</SPAN>&lt;<SPAN style="COLOR: #2b91af">Func</SPAN>&lt;T[]&gt;&gt; expression, <SPAN style="COLOR: #2b91af">BindingFlags </SPAN>invokeAttr, T value, <SPAN style="COLOR: blue">params int</SPAN>[] indices)
{
    <SPAN style="COLOR: blue">object </SPAN>elementValue = (value <SPAN style="COLOR: blue">is </SPAN><SPAN style="COLOR: #2b91af">BaseShadow</SPAN>) ? (value <SPAN style="COLOR: blue">as </SPAN><SPAN style="COLOR: #2b91af">BaseShadow</SPAN>).Target : value;

    self.SetStaticArrayElement(
        ((<SPAN style="COLOR: #2b91af">PropertyInfo</SPAN>)((<SPAN style="COLOR: #2b91af">MemberExpression</SPAN>)(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);
        }
    <SPAN style="COLOR: blue">bool </SPAN>expected = <SPAN style="COLOR: blue">false</SPAN>;
    <SPAN style="COLOR: blue">bool </SPAN>actual;

    actual = <SPAN style="COLOR: #2b91af">MyClass</SPAN>.IsEmpty();

    <SPAN style="COLOR: #2b91af">Assert</SPAN>.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.

No Comments