Este é um post disponível para assinantes MVPGeração automática de testes numa abordagem TDD - Revista .Net Magazine 90
Será apresentado a ferramenta Pex e discutido o uso dela dentro de técnicas de desenvolvimento orientado a testes baseadas em Test Driven Development.
[Artigo já está disponível no Leitor Digital DevMedia®. Clique aqui para acessá-lo]
> Clique aqui para ler todos os artigos da .net Magazine 90
Nos últimos anos, as disciplinas de testes vêm tomando a importância que lhes é de direito. Na prática, está deixando de ser algo passivo, onde simplesmente recebiam dos desenvolvedores os artefatos a serem testados, para se tornarem uma doutrina ativa e obrigatória, guiando o desenho e arquitetura das aplicações antes mesmo de qualquer linha de código ser produzida. O aumento da aceitação e aplicação de TDD (Test-Driven Development) não deixa dúvidas sobre o fato, além da crescente valorização dos profissionais especializados nessa área.
Ferramentas têm sido criadas para apoiar o uso de testes. Frameworks de criação e execução de testes de unidade; bibliotecas para a criação de objetos dublês (stubs e mocks); aplicativos que automatizam testes de interface gráfica e usabilidade; simuladores de chamadas em massa para averiguação de desempenho e estabilidade; entre outros tipos. E dentre esta gama de opções temos o Pex. O Pex é uma ferramenta desenvolvida pela Microsoft Research para a exploração de código e geração automática de testes unitários seguindo uma abordagem de testes de caixa branca.
Neste artigo daremos foco em como trabalhar com o Pex, desde sua utilização mais básica e comum até seu uso com técnicas de TDD. Nos exemplos que iremos demonstrar, faremos uso da ferramenta de testes do Visual Studio (MSTest). No entanto, o Pex foi desenhado para suportar outros frameworks como o MbUnit ou o nUnit.
Testes
de unidade como testes de caixa branca
Testes de caixa branca, também conhecidos como testes estruturais, é um tipo de teste onde são avaliados todos os caminhos lógicos que podem ser tomados dentro do código durante o fluxo de execução. O seu objeto é permitir uma alta cobertura de código, exercitando cada trecho para garantir que ele seja executado pelo menos uma vez, não permitindo que um comportamento que não foi validado entre em produção e que consequentemente possa conter alguma anomalia. Por isso, é essencial que os detalhes de sua implementação interna sejam conhecidos para análise, pois precisamos saber quais parâmetros de entrada devem ser fornecidos para que todos os fluxos de execução sejam percorridos. Isso o difere dos testes de caixa preta, categoria de testes onde não temos ideia de como o sistema foi desenvolvido e por isso testamos apenas a funcionalidade, geralmente apenas através da interface gráfica.
Testes de unidade são categorizados como testes de caixa branca. Os testes de unidade são escritos na forma de exemplos, ou seja, dada uma entrada temos uma determinada saída (um valor ou um comportamento) após a execução da funcionalidade que está sendo verificada (geralmente um método). Fazemos isso de forma que todas as opções de entradas e saídas sejam testadas, obtendo uma alta cobertura do sistema.
Existe um padrão para a escrita de testes unitários. Em primeiro lugar, os parâmetros do teste são preparados, por exemplo, criando objetos e ajustando os valores. Em seguida, vem a execução da funcionalidade que está sendo testada, também conhecida como SUT (System Under Test). Na prática, é a própria chamada ao método que está sendo validado. Por último, temos a confirmação dos valores resultantes dessa execução, ou seja, se o valor que esperávamos é mesmo o que foi obtido no teste. Opcionalmente temos uma quarta etapa que é a limpeza, por exemplo, a remoção de objetos em memória, mas que na prática é raramente necessário para testes de unidade. Este padrão é conhecido como Arrange-Act-Assert, uma referência às fases de preparação, execução e verificação citadas anteriormente.
Vemos na Figura 1 um diagrama exemplificando testes de unidade onde são testados todos os caminhos possíveis do método VerificarNegativo(int). Este método valida se um número inteiro tem valor negativo, retornando true caso isso seja verdade ou false no caso contrário. Podemos ver que para a cobertura total desse método, precisamos de apenas dois casos de testes (exemplos), passando como parâmetros um número negativo e outro positivo (fase “arrange”), executando o método (fase “act”) e confirmando que os resultados são true e false, respectivamente (fase “assert”). Poderíamos também passar outros valores, como zero, -999 ou um milhão, mas na prática esses exemplos de testes adicionais não trariam maior relevância, pois apenas repetiríamos a execução dos fluxos já validados.
Os testes de unidade não são importantes apenas para validar todos os fluxos de execução possíveis, mas também (e principalmente) para servirem de confirmação quando houver alguma alteração de código no sistema. Feita a modificação, os testes de unidade existentes podem ser executados e caso algum deles retorne falha (ou “quebre”, como é geralmente dito) podemos saber qual funcionalidade foi impactada e a partir disso efetuar a correção.
Pex e
testes de unidade parametrizáveis (PUT)
O Pex é uma ferramenta que gera automaticamente os testes de unidade, buscando cobrir todos os caminhos de execução possíveis. Ou seja, dado um método que queremos testar, o Pex poupa o seu trabalho, analisando quais são os parâmetros de entrada relevantes e montando todos os testes necessários.
No entanto, a forma que o Pex gera testes de unidade é um pouco diferente da que costumamos fazer manualmente com os testes de unidade tradicionais. Ele utiliza o conceito de testes de unidade parametrizáveis, ou PUT (Parameterized Unit Test). Um teste de unidade parametrizável nada mais é do que um teste de unidade que aceita parâmetros. A vantagem em se usar testes parametrizáveis, a princípio, é a reutilização de código.
Ao invés de repetir o mesmo código utilizado na execução do SUT em todos os testes de unidade, podemos colocá-lo em apenas um método e chamar esse mesmo método, passando os diversos valores que exercitam a funcionalidade sob teste e verificando os retornos adequados. Esse é um conceito já utilizado, por exemplo, no framework de testes MbUnit, onde podemos passar diversos valores (parâmetros e resultado) para um teste parametrizável através do atributo Row, e com isso o mesmo teste é executado várias vezes mas com valores distintos a cada execução (a Listagem 1 mostra esse exemplo).
Listagem 1. Teste parametrizável com o framework MbUnit
[Test]
[Row(-1, true)]
[Row(1, false)]
public void TesteParametrizavelMbUnit(int numero, bool resultado)
{Sinal sinal = new Sinal();
bool retorno = sinal.VerificarNegativo(numero);
Assert.AreEqual(resultado, retorno);
}
Os testes de unidade parametrizáveis no Pex são decorados com o atributo PexMethod e são criados manualmente pelos desenvolvedores (apesar que a ferramenta pode inicialmente criar um PUT modelo automaticamente, mas que ainda assim depende de alterações do programador para que funcione corretamente).
O Pex usa um teste parametrizável e, para cada diferente conjunto de parâmetros, ele cria um teste unitário tradicional que faz uso do próprio PUT. Um exemplo disso pode ser visto na Listagem 2. Em um primeiro momento, podemos pensar que essa criação de vários testes de unidade tradicional para um mesmo PUT é ruim, pois aumenta o código de testes, mas temos que lembrar que eles são criados automaticamente pela ferramenta, ou seja, não devemos nos preocupar com a manutenção dele. Inclusive, eles são armazenados em um arquivo separado do código-fonte do PUT de forma que, no processo de analise de código que o Pex faz, ele possa incluir e excluir testes de unidade sem impactar o código que é criado pelo desenvolvedor.
"
ATENÇÃO! A exibição deste artigo foi interrompida.
Este é um post disponível para assinantes MVP
Space do autor



0
0
