Introdução

Softwares têm ganhado usuários de diversas áreas, sendo cada vez mais integrados às atividades do cotidiano. Sua aplicação trouxe grandes benefícios e hoje é considerado um bem fundamental para sociedade (HIRAMA, 2012). De acordo com Bartie (2002), os softwares passaram a ganhar complexidade e como sequências desse processo surgiram problemas envolvendo a qualidade. Consequentemente, as organizações têm sido cobradas para desenvolver melhores produtos, em curto prazo e sem aumentar o orçamento do projeto (PRESSMAN; MAXIM, 2016). A qualidade de um produto diz respeito às características que os projetistas especificam para um produto. A qualidade dos materiais, as tolerâncias, especificações e desempenho são fatores que contribuem para a qualidade de um projeto.

Durante a construção de um software, a qualidade deve estar presente em todas as fases do ciclo de vida (especificação, análise de requisitos, modelagem, implementação, teste e manutenção). A qualidade está mais associada aos processos que o produto foi desenvolvido do que o produto em si.

Nesse contexto, uma métrica que têm sido utilizadas para garantir a qualidade dos testes automatizados é a sua cobertura. De acordo com MALAIYA at al. (2002), a cobertura de um código de teste mensura o grau de rigor dos testes. Segundo Yang at al. (2009), a cobertura pode ser utilizada para medir a maneira como é feito os testes de um sistema, sendo utilizado até para inferir sobre a sua confiança na qualidade do software.

Contudo, mesmo com alta cobertura, os testes podem apresentar más escolhas de designs em sua implementação, denominados test smells. Essas inconsistências nos testes automatizados podem aparecer de diferentes formas, é comum que diretrizes sejam quebradas na codificação dos testes, e isso podem interferir de forma negativa na qualidade do teste (GAROUSI, KÜÇÜK, 2018).

Tais fatores são considerados anti-padrões no código de teste. Duplicação de código e baixa modularidade são exemplos de smells em testes, eles podem dificultar a compreensão do código de teste, a sua manutenção, e prejudicar sua qualidade.
Desta forma, o presente estudo visa investigar se há alguma relação entre cobertura de testes e test smells, buscando identificar se há algum tipo de smell que têm mais influência em códigos de teste com uma maior cobertura. O objetivo deste estudo é avaliar a métrica cobertura como um fator de qualidade de teste.

Para tal fim, o trabalho buscou responder as seguintes questões de pesquisa: RQ1: Existe correlação entre a quantidade de tests smells e a cobertura das classes de testes? RQ2: Existe correlação entre a quantidade de tests smells e o tamanho da classe de teste? RQ3: Existe correlação entre a cobertura das classes de teses e o tamanho das classes?

A pesquisa foi estruturada de forma que o Capítulo 2 apresenta o referencial teórico, no Capítulo 3 é abordada a metodologia do trabalho, técnicas, e materiais. A Seção 4 diz respeito aos resultados obtidos no estudo, por fim, no Capítulo 5 são apresentadas as conclusões sobre o trabalho realizado, as principais contribuições e os trabalhos futuros.

2. Referencial Teórico

Neste capítulo são discutidos os principais temas que fundamentam o estudo proposto. Na Seção 2.1 serão abordados conceitos de Test de Software automatizados, a seção seguinte (2.2) trata de Cobertura de teste. O tópico 2.3 consiste na apresentação de definição e tipos de Test Smell.

2.1 Testes de Software

A atividade de testar um software é composta por várias etapas, as quais buscam detectar problemas no sistema e de forma a melhorar o produto em aspectos no que diz respeito à sua qualidade (NETO, 2016). Segundo Pressman e Maxim (2016), a etapa de Teste é constituída de atividades cujo planejamento pode ser feito com antecedência e com execução sistemática.

A qualidade de um software possui uma relação direta com a quantidade de defeitos do produto. Ou seja, um produto que teve um desenvolvimento de qualidade irá gerar um produto com menos defeitos, logo, teremos um produto de alta qualidade. “Para atender aos objetivos de teste a um custo adequado, os conceitos, as estratégias, técnicas e métricas do teste devem ser integradas em um processo de teste definido e controlado” (HIRAMA, 2012, p. 93).

Assim, a intenção do teste é encontrar defeitos para que o software possa ser corrigido. De acordo Neto (2016), nenhum sistema está isento ter problemas, por isso, se um caso de teste não encontra problemas, significa que o teste não foi bem projetado e precisa ser redefinido. Logo, um bom caso de teste é caracterizado como aquele que tem grande probabilidade de descobrir imprecisões.

A fase de teste é imprescindível e tem grande relevância na produção de software de qualidade. Contudo, a prática testar de forma superficial por causa do cronograma planejado é comum nas empresas, e é provável que esses defeitos sejam detectados pelo cliente, fazendo com que diminua a credibilidade do software. (NETO, 2016; HIRAMA, 2012).

Quanto ao processo, testes devem ser sistematizados, planejados e executados conforme o escopo definido. Eles devem ser uma etapa da Engenharia de Software (ES) e como qualquer fase do processo, deve ter uma equipe responsável que seja especializada no assunto, pois o teste exige conhecimento, planejamento, projeto, execução, monitoramento, recursos, e é importante que haja comunicação com os outros processos da ES. Tais procedimentos eram vistos pelos desenvolvedores como um desperdício de tempo, já que os produtos eram retornados para o setor de programação onde deveriam ser corrigidos (BARTIE, 2002; WAZLAWICK, 2013; CRESPO et al., 2004).

Apesar da sua relevância no desenvolvimento de sistemas, a fase de testes apresentam algumas dificuldades para ser executada (HIRAMA, 2012). Alguns dos fatores são o conhecimento específico exigido na área e o curto cronograma do projeto. Testar um sistema não é algo simples, “pode ser mais difícil de elaborar bons casos de teste do que produzir o próprio software” (WAZLAWICK, 2013, p.290). Nesse sentido, é necessário que o processo de teste seja organizado de forma sistematizada e que haja gerenciamento de cada fase.

As principais estratégias de testes para software de acordo com Pressman (2006) são: Teste de Unidade, Teste de Integração, Teste de Validação e Teste de Sistema. Segundo Sommerville (2011), o processo de testar compreende duas modalidades: testes manuais e automatizados. No primeiro método, um profissional executa o sistema com dados fictícios e comparam os resultados obtidos com os valores esperados, as divergências encontradas são reportadas aos programadores da rotina. Já nos teste automatizados, o testadores criam os casos de teste através dos scripts de código.

A segundo técnica é considerada mais eficiente uma vez que não exigem tanta atenção do testador a cada correção e versão atualizada do produto. Contudo, se não for bem elaborado, pode ocasionar problemas nos testes que inviabilizam o objetivo para o qual o teste foi proposto: identificar falhas no sistema.

Pesquisas apontam que em muitos casos, os testes automatizados têm resultados mais eficientes do que os testes manuais. No mercado de desenvolvimento de software, já existem ferramentas específicas para o desenvolvimento dos scripts de teste, como é o caso da ferramenta JUnit . O sucesso dos testes automatizados pode ser explicado pela a falta de interferência humana, segundo Garousi e Küçük (2018).

Segundo pesquisas, o uso de teste automatizado por indústria tem se tornado uma realidade cada vez mais presente. Testes automatizados de alta qualidade proporcionam facilidade nas atividades de manutenção do software (PAGE; JOHNSTON; ROLLISON, 2008).

2.2 Cobertura de Teste

Testes de unidade como o JUnit, por exemplo, usam métodos de teste com caminhos específicos na estrutura de controle de um componente com o objetivo de assegurar maior cobertura ao teste, e consequentemente maior capacidade em detectar defeitos no código (PRESSMAN; MAXIM, 2016).

Para garantir que um código seja testado por completo, é feito a análise de cobertura de teste, verificando se o teste teve a capacidade de analisar o sistema inteiro, passando por todas a linhas de código por pelo menos uma vez (SOMMERVILLE, 2011). Assim, “se determinado código-fonte possui 100 linhas e durante a execução dos testes conseguimos executar 87, nossos testes obtiveram 87% de cobertura do código-fonte” (BARTIE, 2002, p.127).

Conforme Sommerville (2011), a cobertura de um teste consiste em teste mostrar a eficiência dos testes em fazer com que as declarações do código-fonte sejam executadas. Para garantir maior cobertura, o testador deve: testar todas as operações associadas ao objeto; especificar e verificar o valor de todos os atributos associados ao objeto; colocar o objeto em diversos estados possíveis, simulando efeitos e eventos de mudança.

Segundo Bartie (2002), a cobertura também pode ser utilizada como um controlador de qualidade dos casos de teste e suas versões evoluídas nos sucessivos ciclos de interação. Nesse sentido, mesmo sendo uma tarefa desafiadora, desenvolvedores de teste buscam alcançar 100% da execução do código fonte, ainda que cobertura total (100%) não garante a ausência de defeitos (YANG ET AL., 2007).

2.3 Test Smell

O termo Smell foi cunhado por Kent Beck enquanto ajudava Martin Fowler com o livro Refactoring (SMELL, 2018). Martin Fowler em conjunto com Kent Beck levantam o problema em estudos sobre a refatoração do código, chegando a uma analogia com “bad smell”. Bad Smells são problemas de implementação no código que podem gerar possíveis complicações no futuro.
Nesse sentido, Tests smells estão para os testes assim como os Bad Smells estão para o código fonte.

Assim como a codificação, trabalhar com de script de teste é uma tarefa cansativa, que está suscetível a erros. No estudo de Tufano et al. (2016), os Tests Smells são definidos como escolhes pobres de design, em que pode afetar de forma negativa a sua compreensão e manutenção. Apesar de existir ferramentas que deem suporte à detecção de smells em código, há poucas ferramentas disponíveis para a detecção desses fatores em testes.

Os Test Smells são classificados como: Assertion Roulette, Conditional Test Logic, Constructor Initialization, Default Test, Duplicate Assert, Eager Test, Empty Test, Exception Handling, General Fixture, Ignored Test, Lazy Test, Magic Number Test, Mystery Guest, Redundant Print, Redundant Assertion, Resource Optimism, Sensitive Equality, Sleepy Test, Unknown Test, Test Run War, Indirect Testing, For Testers Only, Test Code Duplication.

3. Metodologia

O seguinte capítulo detalha métodos, ferramentas e processos da pesquisa, de tal forma que a Subseção 3.1 refere-se às técnicas de coleta de dados, a Subseção 3.2 apresenta as transformações necessárias dos dados, e a Subseção 3.3 aborda a maneira como os dados foram analisados.

3.1 Coleta dos dados

Para a coleta de dados da pesquisa, foram utilizados as ferramentas Test Smell Detector e um plugin de cobertura do Maven. Para tanto, foram selecionados 6 projetos, dentre os quais, um deles é o Joda-Time e cinco são bibliotecas do projeto Apache de uso comum no Java.

Os critérios utilizados para selecionar os projetos foram: linguagem de programação Java, utilizar o Maven, conter testes de unidade com JUnit, ser open source, possuir documentação, utilização por parte da comunidade industrial e acadêmica, e contarem com um grande número de contribuidores. A tabela a seguir apresenta a relação das ferramentas selecionadas, assim como informações gerais sobre as mesmas:

Projeto Descrição Site oficial
Joda Time O Joda-Time é a biblioteca padrão de data e hora do Java antes do Java SE 8. https://www.joda.org/joda-time
col 2 is centered $12
col 3 is right-aligned $1

Os projetos selecionados passaram por uma inspeção de Test Smell feita pela ferramenta Test Smell Detector. Essa aplicação é de código aberto, e tem como entrada um arquivo .csv contendo o diretório das classes implementadas com suas respectivas classe de testes, e como saída a ferramenta gera um arquivo .csv onde é mostrado se cada classe de teste apresenta ou não (true ou false) um determinado tipo de test smells.

Antes da inspeção dos tests smells, foram feitas algumas implementações ao código do Test Smell Detector a fim de obter o número de cada tipo de smell presente em cada classe de teste, uma vez que a ferramenta retorna apenas um booleano para informar se a classe apresentava ou não aquele tipo de problema.

Também foram feitas modificações para melhorar o fluxo da ferramenta no que diz respeito ao processo de criar o arquivo da entrada com as classes e seus respectivos testes. Essa tarefa vinha sendo feito de forma separada em outras ferramentas ou até mesmo de forma manual, mas foi automatizado e integrado ao projeto principal do Test Smell Detector.

Para automatizar esse processo, foi necessário adicionar o Test File Detector e o Test File Mapping no projeto principal, como mostrado na Figura 1. O Teste File Detector é uma aplicação que tem como entrada um arquivo contendo o diretório do projeto e o mesmo retorna a lista de classes de teste (ArquivoA.csv) em um determinado projeto. Já Test File Mapping é uma ferramenta que retorna um arquivo (ArquivoB.csv) com a lista de classes de teste e as classes de produção, as quais são identificadas a partir do arquivo de entrada da lista de teste que foi gerada pela ferramenta anterior.

Figura 1: Fluxograma das etapas realizadas em cada projeto

Figura 1: Fluxograma das etapas realizadas em cada projeto

Após rodar o Test Smell Detector e obter o arquivo com o resultado do tamanho das classes de teste e a quantidade de cada tipo de Test Smell por classe de teste do projeto, foi feita a análise da cobertura pelo plug-in do Maven, gerando o ArquivoD.csv.

Em seguida, os arquivos ArquivoC.csv e ArquivoD.csv foram mesclados para gerar um único arquivo contendo todas as informações coletadas sobre um projeto. Para obter o ArquivoE.csv de cada projeto, foi necessário criar um código em Shell Script para repetir o processo descrito na Figura 1 em cada pasta e posteriormente unir os arquivos de todos os projetos em um único documento como apresentado pelo fluxograma da imagem a seguir:

Figura 2: Processo para gerar o arquivo final

Figura 2: Processo para gerar o arquivo final

2.2 Transformações dos dados

Quanto à transformação dos dados, não foi necessário nenhum tipo de tratamento, pois tanto a ferramenta de identificação de teste Smell, quanto o plugin de cobertura foram bastante eficiente na geração dos arquivos.

2.3 Descrição dos dados

O arquivo final conteve 480 linhas e 27 colunas, totalizando um arquivo de 53,2 KB. A primeira linha contém o cabeçalho das informações (App, TestFileName, ProductionFileName, os 21 tipos de Smells que a ferramenta dá suporte, totall de tests smells por classe, o tamanho da classe e teste e cobertura). A análise desses dados foi feita utilizando a linguagem R, na IDE do RStudio, escritos no formato R Markdown (extensão .Rm). A Tabela 3 possui uma breve descrição dos tipos de dados em cada coluna do arquivo de dados:

TABELA

2.4 Análise dos dados

A presente seção consiste em descrever a análise dos dados com o objetivo de responder cada questão de pesquisa formulada na Seção 1.

2.4.1 RQ1: Existe correlação entre a quantidade de tests smells e a cobertura das classes de testes?

Como pretendemos estimar o relacionamento entre as variáveis numéricas quantidade de teste smells e a cobertura, foi utilizado o teste de correlação. O teste de correlação…

IMGAEM3

2.4.2 RQ2: Existe correlação entre a quantidade de tests smells e o tamanho da classe de teste?

aaaaa

2.4.3 RQ3: Existe correlação entre a cobertura das classes de teses e o tamanho das classes?

4. Resultados

4.1 RQ1: Existe correlação entre a quantidade de tests smells e a cobertura das classes de testes?

4.2 RQ2: Existe correlação entre a quantidade de tests smells e o tamanho da classe de teste?

4.3 RQ3: Existe correlação entre a cobertura das classes de teses e o tamanho das classes?

  • Apresentar análise exploratória e resultado de testes estatístico
  • Fazer box splot nos gráfico do quartil

5. Conclusão

Futuramente, pensamos em gerar um relatório não apenas com a quantidade de cada tipo de smells por classe, mas identificar a linha em que os smells foram encontrados.

6. Referências