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)