André Alves de Lima

Talking about Software Development and more…

Dica rápida: Avaliando parâmetros de saída com PrivateObject

Eu já escrevi uns tempos atrás sobre a substituição de Private Accessors por PrivateObjects quando escrevemos unit tests a partir do Visual Studio 2013. A Microsoft eliminou a funcionalidade de Private Accessors a partir dessa versão do Visual Studio, então, utilizar PrivateObject é a saída para realizar testes de métodos privados.

Porém, algo que não foi abordado naquele artigo é: como avaliar parâmetros de saída com PrivateObject? Ou seja, em métodos privados, como testar se o valor de parâmetros de saída (output parâmeters – aqueles parâmetros identificados com a palavra reservada “out“) está correto a partir dos nossos unit tests? É justamente isso que eu vou mostrar nesta dica rápida de hoje.

Para demonstrar essa funcionalidade, vou expandir o exemplo daquele artigo que eu escrevi sobre PrivateObjects em 2013. Digamos que o método privado que queremos testar tem um parâmetro de saída:

    public class ClasseASerTestada
    {
        private bool MetodoPrivado(bool parametro, out string parametroSaida)
        {
            parametroSaida = parametro.ToString();
            return parametro;
        }
    }

Viu só o parâmetro de saída ali? Nesse caso ele simplesmente retorna um ToString do parâmetro booleano passado anteriormente. Nada muito útil, mas, o foco desta dica rápida está no unit test, e não na funcionalidade do método em si.

Agora, no nosso projeto de unit tests, como fica para testarmos o valor retornado por “parametroSaida“? (obs: não vou abordar como criar um projeto de unit tests porque eu já expliquei no outro artigo inicial sobre PrivateObjects – caso você queira conferir, clique aqui):

        [TestMethod]
        public void TestMethodParametroSaida()
        {
            ClasseASerTestada obj = new ClasseASerTestada();
            PrivateObject privateObj = new PrivateObject(obj);

            // Isso não funciona!!
            var parametroSaida = string.Empty;
            var retorno = (bool)privateObj.Invoke("MetodoPrivado", new object[] { false, parametroSaida });
            Assert.IsFalse(retorno);
            Assert.AreEqual("False", parametroSaida);
        }

Porém, como você pode perceber pelo comentário no código, essa sistemática não funciona. O PrivateObject até consegue encontrar o método com o parâmetro de saída, mas, o valor não é retornado.

Qual é a alternativa? Bom, na verdade, a única opção é não utilizar PrivateObject! Como indicado nesta discussão do StackOverflow, temos que criar um delegate para o método e testá-lo utilizando uma chamada a “GetMethod“. Não entendeu? Eu explico.

Primeiro temos que criar (fora do TestMethod) um delegate com exatamente a mesma assinatura do método que estamos querendo testar. No nosso caso, ficaria assim:

        delegate bool MetodoPrivado(bool parametro, out string parametroSaida);

Depois, dentro do unit test, temos que criar uma instância desse delegate (utilizando a chamada Delegate.CreateDelegate apontando para a nossa instância de “ClasseASerTestada“). Aí podemos chama-lo normalmente e avaliar o valor do parâmetro de saída sem nenhum problema:

            // Isso funciona!!
            MetodoPrivado metodoPrivado = (MetodoPrivado)Delegate.CreateDelegate(
                typeof(MetodoPrivado),
                obj,
                typeof(ClasseASerTestada).GetMethod("MetodoPrivado", System.Reflection.BindingFlags.NonPublic | System.Reflection.BindingFlags.Instance));
            var parametroSaida = string.Empty;
            var retorno = metodoPrivado.Invoke(false, out parametroSaida);
            Assert.IsFalse(retorno);
            Assert.AreEqual("False", parametroSaida);

Veja só como fica o código completo do unit test:

    [TestClass]
    public class UnitTests
    {
        delegate bool MetodoPrivado(bool parametro, out string parametroSaida);

        [TestMethod]
        public void TestMethodParametroSaida()
        {
            ClasseASerTestada obj = new ClasseASerTestada();
            PrivateObject privateObj = new PrivateObject(obj);

            // Isso não funciona!!
            //var parametroSaida = string.Empty;
            //var retorno = (bool)privateObj.Invoke("MetodoPrivado", new object[] { false, parametroSaida });
            //Assert.IsFalse(retorno);
            //Assert.AreEqual("False", parametroSaida);

            // Isso funciona!!
            MetodoPrivado metodoPrivado = (MetodoPrivado)Delegate.CreateDelegate(
                typeof(MetodoPrivado),
                obj,
                typeof(ClasseASerTestada).GetMethod("MetodoPrivado", System.Reflection.BindingFlags.NonPublic | System.Reflection.BindingFlags.Instance));
            var parametroSaida = string.Empty;
            var retorno = metodoPrivado.Invoke(false, out parametroSaida);
            Assert.IsFalse(retorno);
            Assert.AreEqual("False", parametroSaida);
        }
    }

E essa foi a dica rápida de hoje, onde eu mostrei para você como fazer para testar parâmetros de saída de métodos privados. Espero que te ajude.

Até a próxima!

André Lima

Deixe uma resposta

O seu endereço de e-mail não será publicado. Campos obrigatórios são marcados com *