Como Atribuír Valores A Elementos De Um Array De Um Tipo Privado Usando Os Acessores Do Visual Studio

O Visual Studio usa a ferramenta Publicize para criar acessores públicos para membros e tipos privados de um determinado tipo.

Mas quando se tenta definir o valor de um elemento de um array privado de elementos de um tipo privado, a situação complica-se.

Imagine-se este hipotética classe a testar:

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;
    }
}

Se se quiser escrever um teste para o caso em que o array tem entradas “não vazias”, vai ser necessário primeiro inicializar o array.

Usando os acessores gerados pelo Visual Studio, o teste pode ser escrito desta froma:

[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);
    }
}

Mas o teste vai falhar porque, apesar dos elementos do array privado myArray poderem ser lidos como instâncias de MyClass_Accessor.MyInnerClass, não podem ser escritos como tal.

Para o fazer, o teste tem de ser escrito da seguinte forma:

[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);
    }
}

Mas, deste modo, perdemos a característica fortemente tipada que nos dão os acessores gerados pelo Visual Studio porque é necessário escrever o nome do campo myArray.

Porque o acessor para o campo é uma propriedade, podem-se escrever alguns métodos de extensão para obter o nome do campo sem que seja necessário escrevê-lo como uma string. Algo assim:

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);
    }
}

Sendo assim, o teste tomaria esta forma:

[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);
    }
}

Não é o mesmo que a primeria forma, mas é fortemente tipado e, se se mudar o nome ou o tipo do campo myArray, obterem-se um erro de compilação e não um erro de execução do teste.

Podem encontrar isto (e mais) em PauloMorgado.TestTools no CodePlex.

Leave a Comment

(requerido) 
(requerido) 
 
(opcional)
(requerido) 
If you can't read this number refresh your screen
Enter the numbers above: