10 12 2015
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
Dica rápida: Resolvendo o erro DbProviderFactories section can only appear once per config file Como escanear documentos com o C#? (digitalização de documentos)