Desenvolvendo Bons Testes Automatizados Exame de Qualificação Universidade de São Paulo (USP) Instituto de Matemática e Estatística (IME) Departamento de Ciência da Computação (DCC) Área de Concentração: Ciência da Computação Aluno: Paulo Cheque Bernardo Orientador: Fabio Kon Exame de Qualificação: Desenvolvendo Bons Testes Automatizados Banca examinadora: Titulares : Prof. Dr. Fabio Kon – IME-USP (Orientador) Prof. Dr. Alfredo Goldman vel Lejbman – IME-USP Prof. Dr. Marco Aurélio Gerosa – IME-USP Suplentes : Prof. Dr. Alan Mitchell Durham – IME-USP Prof. Dr. Paulo José da Silva e Silva – IME-USP Prof. Dr. Marcelo Finger – IME-USP Prefácio Este texto para qualificação possui uma estrutura de capítulos definida, mas que está sujeita a alterações devido ao aprendizado com o decorrer do estudo e com possíveis sugestões da banca. Alguns destes capítulos estão finalizados e sujeitos a pequenas alterações e a adição de mais conteúdo, outros estão incompletos e contém apenas alguns tópicos, seções ou esboços do conteúdo que se espera para aquele trecho do trabalho. Eles estão documentados apenas para referência do que se espera do trabalho e abertos para críticas. Para facilitar a identificação destes capítulos, eles estão marcados com o texto "ESBOÇO". A parte I, que é composta de três capítulos introdutórios, está praticamente finalizada. A parte II que contém 7 capítulos ainda não foi iniciada, contendo apenas capítulos e seções para referência. Já a parte III contém capítulos finalizados e de referência: o capítulo de Métricas está finalizado enquanto o capítulo Estratégias para Automação de Testes) não foi iniciado. Já o capítulo Considerações Finais, possui constatações consistentes já adquiridas com o estudo atual, no entanto está sujeito a grandes alterações devido ao grande trabalho que ainda precisa ser realizado. i ii Resumo Testes de Software é uma área de estudo que tem crescido significativamente nos últimos tempos, em especial a automação de testes que está cada vez mais em evidência devido à agilidade e qualidade que eles podem trazer para o desenvolvimento de sistemas de software. No entanto, pouco conhecimento e anti-padrões de testes automatizados podem trazer diversos problemas como perda de tempo e estouro de prazos, além de não contribuirem de forma efetiva com a qualidade do software. Este trabalho introduz o assunto de testes automatizados com a descrição dos principais conceitos do tema e da discussão do seu relacionamento com outras áreas de estudo, como Métodos Ágeis e Software Livre, apresentando as principais características e benefícios destas prática. Em seguida, é apresentado padrões, técnicas e práticas recomendadas para a criação de testes automatizados de qualidade, que proporcionam efetivamente os melhores resultados para o desenvolvimento de software. Além disso, também é abordado o tema de gerenciamento dos testes dentro de novos projetos ou projetos legados, para auxiliar a evolução e guiar a equipe para as melhores técnicas e práticas de desenvolvimento de testes. Palavras-chave: Engenharia de Software, Programação eXtrema, XP, Ágil, Testes Automatizados, TAD, POUT, TFD, Métodos ágeis, Desenvolvimento Dirigido por Testes, TDD, Desenvolvimento Dirigido por Comportamento, BDD, Métricas iii iv Sumário Lista de Figuras ix Lista de Tabelas xi I Introdução e Conceitos 1 1 Introdução 1.1 Objetivos . . . . . . . . 1.2 A quem se destina . . . . 1.3 Trabalhos Relacionados . 1.4 Andamento do Trabalho . . . . 3 3 3 3 4 . . . . . . . 5 5 6 6 7 7 8 9 . . . . . . . . 11 11 11 11 12 12 12 12 12 2 3 II 4 . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . Testes Automatizados 2.1 Cenário Comum de Desenvolvimento . . 2.2 Uma Nova Abordagem . . . . . . . . . . 2.3 História . . . . . . . . . . . . . . . . . . 2.4 Métodologias Ágeis de Desenvolvimento 2.4.1 Programação eXtrema . . . . . . 2.5 Software Livre . . . . . . . . . . . . . . 2.6 Definições de Termos e Siglas . . . . . . Tipos de Testes Automatizados 3.1 Teste de Unidade . . . . . . . . 3.2 Teste de Integração . . . . . . . 3.3 Teste de Interface de Usuário . . 3.3.1 Teste de Layout . . . . . 3.4 Teste de Aceitação . . . . . . . 3.5 Teste de Sanidade (Sanity Tests) 3.6 Teste Fumaça (Smoke Tests) . . 3.7 Teste de Sistema . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . Práticas, Padrões e Técnicas (ESBOÇO) Bons Testes Automatizados (ESBOÇO) 4.1 Filosofia da Automação de Testes . 4.2 Princípios Básicos . . . . . . . . . . 4.3 Cheiros . . . . . . . . . . . . . . . 4.4 Padrões . . . . . . . . . . . . . . . . . . . . . . . . . . . v 13 . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 15 15 15 15 15 4.5 5 Anti-Padrões . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 16 Padrões de Automação (ESBOÇO) 5.1 Objetos Dublês . . . . . . . . 5.1.1 Introdução . . . . . . 5.1.2 Benefícios . . . . . . 5.1.3 Tipos De Dublês . . . 5.1.4 Anti-Padrões . . . . . 5.2 Expressão Regular . . . . . . 5.3 Listas . . . . . . . . . . . . . 5.4 Strings . . . . . . . . . . . . . 5.5 Algoritmos Matemáticos . . . 5.6 Algoritmos Aleatórios . . . . 5.7 Processos assíncronos . . . . . 5.8 Objetos . . . . . . . . . . . . 5.9 Aspectos . . . . . . . . . . . . 5.9.1 . . . . . . . . . . . . 5.10 Web Services . . . . . . . . . 5.11 Reflexão . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 17 17 17 17 17 17 17 17 18 18 18 18 18 18 18 18 18 6 Testes de Unidade (ESBOÇO) 19 6.1 Preparação do Ambiente . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 19 6.2 Testes clássicos vs Testes completamente isolados . . . . . . . . . . . . . . . . . . . . . 19 7 Testes de Banco de Dados (ESBOÇO) 21 7.1 Arcabouços Objeto-Relacional . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 21 7.2 Stored Procedures e Triggers . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 21 7.3 Replicação de Dados . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 21 8 Testes de Interface de Usuário 23 8.1 Responsabilidades da UI . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 23 8.2 Paradigmas de Automação . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 24 9 Testes de Aceitação (ESBOÇO) 27 10 Técnicas de Escrita de Testes Automatizados (ESBOÇO) 10.1 Testes depois da Implementação (TAD) . . . . . . . . 10.2 Testes antes da Implementação (TFD) . . . . . . . . . 10.3 Desenvolvimento Dirigido por Testes (TDD) . . . . . . 10.3.1 Introdução . . . . . . . . . . . . . . . . . . . 10.3.2 Refatoração . . . . . . . . . . . . . . . . . . . 10.3.3 Fluxo . . . . . . . . . . . . . . . . . . . . . . 10.4 Desenvolvimento Dirigido por Comportamento (BDD) III Adotando e Gerenciando Testes Automatizados . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 29 29 29 29 29 30 30 30 31 11 Métricas 33 11.1 Métricas para Testes Automatizados . . . . . . . . . . . . . . . . . . . . . . . . . . . . 33 vi 12 Estratégias para Automação de Testes (ESBOÇO) 12.1 Por onde começar . . . . . . . . . . . . . . . . 12.2 Baterias de Testes . . . . . . . . . . . . . . . . 12.3 Novos Projetos . . . . . . . . . . . . . . . . . 12.4 Projetos Legados . . . . . . . . . . . . . . . . 12.5 Software Livre . . . . . . . . . . . . . . . . . 12.6 Garantia de Qualidade . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 37 37 37 37 37 38 38 13 Considerações Finais 39 13.1 Pontos para Pesquisa . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 39 13.2 Trabalhos Futuros . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 40 vii viii Lista de Figuras 12.1 Estratégia não aconselhada [WWC] . . . . . . . . . . . . . . . . . . . . . . . . . . . . 37 12.2 Boa estratégia [WWC] . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 38 ix x Lista de Tabelas 11.1 Objetivo vs Métrica (Goal vs Metric) . . . . . . . . . . . . . . . . . . . . . . . . . . . . 35 xi Parte I Introdução e Conceitos 1 Capítulo 1 Introdução Este trabalho discute a prática de testes automatizados de software seguindo a linguagem utilizada pelas comunidades de métodos ágeis. O tema deste trabalho é muito abrangente e intersecta muitas áreas de estudo de engenharia de software e sistemas de software assim como atinge diversas comunidades, por isso este capítulo contextualiza o tema apresentando seus objetivos, indicando trabalhos pertinentes para um melhor aproveitamento da leitura e apresentar o trabalho. 1.1 Objetivos Este trabalho propõe primeiramente fazer um apanhado geral da prática de testes automatizados e documentar os conceitos mais importantes. Os capítulos Introdução aos Testes Automatizados e Tipos de Testes Automatizados focam em introduzir os conceitos e a linguagem utilizada por este trabalho. Outro objetivo é reunir os aspectos mais importantes para o desenvolvimento de bons testes automatizados, além de detalhar as situações de testes mais comuns e apresentar soluções para problemas rotineiros. A parte II é dedicada a isto, finalizando com técnicas de escrita de testes, como TDD e BDD. Nesta seção, todas os exemplos de código utilizam ferramentas e arcabouços livres. Um outro ponto abordado por este trabalho é em relação a adoçao e gerenciamento de testes automatizados dentro de um projeto. Estudo de métricas de acompanhamento Este trabalho não pretende evidenciar quais técnicas de escrita de testes são mais vantajosas, nem comparar testes automatizados com métodos formais e matemáticos. Também não faz parte do escopo deste trabalho ensinar ou detalhar ferramentas e arcabouços de testes automatizados. 1.2 A quem se destina Este trabalho aborda desde temas técnicos de computação, a visão e importância dos clientes e estratégias de gerenciamento de projetos de software relacionadas com testes automatizados. Desta maneira, o trabalho é pertinente para programadores, gerentes de projetos e negócios e donos de produtos de software, além de pesquisadores e curiosos. 1.3 Trabalhos Relacionados Testes Automatizados é uma prática disseminada principalmente pela comunidade de métodos ágeis, por isso o estudo desta área do conhecimento pode aprofundar o aproveitamento da leitura deste trabalho. Existem trabalhos com reflexões [dS07] e experiências [Fil08] sobre o assunto, além de outros que focam em outras práticas pontuais, como métricas de software [Sat07]. Em relação ao tema específico 3 do trabalho, existem estudos [Gue05] e livros [JCM07][Mes07] que servem de referência e completam o assunto. 1.4 Andamento do Trabalho No decorrer dos próximos meses, o autor deve completar os capítulos e seções pendentes, além de refinar e completar o material atual. No entanto, eventualmente a estrutura de tópicos e organização do trabalho pode mudar, devido ao aprofundamento do conhecimento. O estudo deve continuar através da leitura de novos livros e artigos, aĺém do trabalho de descrever as experiências do autor obtida nos últimos meses. 4 Capítulo 2 Testes Automatizados Controlar a qualidade de sistemas de software é um grande desafio devido à alta complexidade dos produtos e às inúmeras dificuldades relacionadas ao processo de desenvolvimento, que envolve questões humanas, técnicas, burocráticas, de negócio e políticas. Idealmente, os sistemas de software devem não só fazer corretamente o que o cliente precisa, mas também fazê-lo de forma segura, eficiente e escalável e serem flexíveis, de fácil manutenção e evolução. Salvo honrosas exceções, na indústria de software brasileira, estas características são muitas vezes asseguradas através de testes manuais do sistema após o término de módulos específicos ou até mesmo do sistema inteiro. Esta abordagem manual e ad hoc leva à ocorrência de muitos problemas e deve ser evitada. Este trabalho se inspira na filosofia dos Métodos Ágeis de Desenvolvimento [B+ 01] de Software e em práticas recomendadas pela Programação eXtrema (XP), com foco em Testes Automatizados, que é uma técnica voltada principalmente para a melhoria da qualidade dos sistemas de software. 2.1 Cenário Comum de Desenvolvimento O modo convencional de desenvolvimento de uma funcionalidade é estudar o problema, pensar em uma solução e, em seguida, implementá-la. Após esses três passos, o desenvolvedor faz testes manuais para verificar se está tudo funcionando como o esperado. É normal que erros sejam detectados ao longo do processo de desenvolvimento, então os desenvolvedores precisam encontrar o erro, corrigi-lo e refazer o conjunto de testes manuais. Além disso, para verificar se algum erro deixou de ser identificado durante a fase de desenvolvimento, antes de colocar o sistema em produção, é muito comum submeter o software a um processo de avaliação de qualidade. Esse controle de qualidade geralmente é realizado com o auxílio de testes manuais executados por desenvolvedores, usuários ou mesmo por equipes especializadas em testes. Este cenário é comum principalmente em empresas que utilizam metodologias rígidas que possuem fases bem definidas, geralmente derivadas do modelo de cascata. Este tipo de metodologia freqüentemente leva à aparição de diversos problemas recorrentes nas indústria de software, tais como atrasos nas entregas, criação de produtos com grande quantidade de erros e dificuldade de manutenção e evolução. Isto deve-se às dificuldades da realização de testes manuais. A execução manual de um caso de teste é rápida e efetiva, mas a execução e repetição de um vasto conjunto de testes manualmente é uma tarefa muito dispendiosa e cansativa. É normal e compreensivo que os testadores não verifiquem novamente todos os casos a cada mudança significativa do código; é deste cenário que surgem os erros de software, trazendo prejuízo para as equipes de desenvolvimento que perdem muito tempo para identificar e corrigir os erros e também prejuízo para o cliente que, entre outros problemas, sofre com constantes atrasos nos prazos combinados e com a entrega de software de qualidade duvidosa. 5 Mas o aspecto mais crítico deste cenário é o efeito “bola de neve”. Como é necessário muito esforço para executar todo o conjunto de testes manuais, dificilmente a cada correção de um erro, a bateria de testes será executada novamente como seria desejável. Muitas vezes, isso leva a erros de regressão (erros em módulos do sistema que estavam funcionando corretamente e deixam de funcionar). A tendência é este ciclo se repetir até que a manutenção do sistema se torne uma tarefa tão custosa que passa a valer a pena reconstruí-lo completamente. 2.2 Uma Nova Abordagem Muitos métodos ágeis, como Lean [PP03], Scrum [SB01] e XP [BA04] recomendam que todas as pessoas de um projeto (programadores, gerentes, equipes de homologação e até mesmo os clientes) trabalhem controlando a qualidade do produto todos os dias e a todo momento, pois acreditam que prevenir defeitos é mais fácil e barato que identificá-los e corrigi-los. A Programação eXtrema, em particular, recomenda explicitamente testes automatizados para ajudar a garantir a qualidade dos sistemas de software. Vale ressaltar ainda, que os métodos ágeis não se opõem a quaisquer revisões adicionais que sejam feitas para aumentar a qualidade. Testes automatizados são programas ou scripts simples que exercitam funcionalidades do sistema sendo testado e fazem verificações automáticas nos efeitos colaterais obtidos. A grande vantagem desta abordagem, é que todos os casos de teste podem ser facilmente e rapidamente repetidos a qualquer momento e com pouco esforço. A reprodutibilidade dos testes permite simular identicamente e inúmeras vezes situações específicas, garantindo que passos importantes não serão ignorados por falha humana e facilitando a identificação de um possível comportamento não desejado. Além disso, como os casos para verificação são descritos através de um código interpretado por um computador, é possível criar situações de testes bem mais elaboradas e complexas do que as realizadas manualmente, possibilitando qualquer combinação de comandos e operações. A magnitude dos testes pode também facilmente ser alterada. Por exemplo, é trivial simular centenas de usuários acessando um sistema ou inserir milhares de registros em uma base de dados, o que não é factível com testes manuais. Todas estas características ajudam a solucionar os problemas encontrados nos testes manuais, diminuindo a quantidade de erros e aumentando a qualidade do software. Como é relativamente fácil executar todos os testes a qualquer momento, mudanças no sistema podem ser feitas com segurança, o que aumenta a vida útil do produto. Na grande maioria das vezes, os testes automatizados são escritos programaticamente, por isso é necessário conhecimento básico de programação, mas existem diversas ferramentas gráficas que escondem os detalhes de implementação permitindo que clientes e outros profissionais que não sejam desenvolvedores também consigam escrever seus próprios testes. Ainda existem as ferramentas que separam a descrição do cenário de teste da sua implementação, desta forma os clientes e os desenvolvedores são incentivados a trabalhar como uma única equipe para integrar os conceitos. Em todo o decorrer deste trabalho, todas os exemplos descritos utilizam ferramentas e arcabouços livres. 2.3 História A idéia de testar manualmente sempre existiu, desde da época dos cartões perfurados até os softwares de milhões de linhas de código, pois é uma prática básica e natural das ciências exatas. A necessidade de criar scripts ou programas para testar cenários específicos também não é nova, no entanto, a idéia de criar testes automatizados como um módulo adicional do sistema a ser testado é relativamente nova, com início em meados da década de 90. Muitas práticas de desenvolvedores já evidenciavam a necessidade da criação destes testes, tais como trechos de código utilizados para imprimir valores de variáveis (co6 mandos print) e métodos main espalhados em trechos internos do código fonte para fazer execuções pontuais do programa. A prática de testes automatizados se popularizou devido a arcabouços que facilitam a escrita e execução dos testes, além de não poluir o código fonte e não interferir no comportamento original do sistema. O primeiro arcabouço que se tem referência é o Taligent Test Framework criado por Alan Liu e David McCusker em 1991 e publicado em 1995 [SC95], mas que não se popularizou. Por volta de 1994, Kent Beck criou o arcabouço SUnit para linguagem SmallTalk [Bec94], que até hoje é utilizado como referência para arcabouços semelhantes para outras linguagens de programação. De tão simples que o SUnit era, Kent Beck não imaginava que ele teria algum valor, mas ao utilizálo para testar algumas estruturas de dados de um sistema em que trabalhava, ele acabou encontrando um número considerável de defeitos. A partir deste caso de sucesso, a prática de testes automatizados começou a se disseminar. Em 1998 Kent Beck e Erich Gamma desenvolveram o arcabouço JUnit para popular linguagem Java, inspitado no SUnit, e então que testes automatizados começou realmente a se popularizar. Hoje, testes automatizados é considerado por diversas metodologias, tais como metodologias ágeis, como uma prática básica para garantir a qualidade de um software. Muito tem se estudado, novos conceitos e técnicas de desenvolvimento foram criadas, e as ferramentas estão cada vez mais poderosas, facilitando não apenas a escrita de testes para trechos específicos de código, mas também para a integração de módulos, interfaces gráficas e bancos de dados. 2.4 Métodologias Ágeis de Desenvolvimento A prática de testes automatizados tem sido difundida principalmente pelas comunidades ágeis de desenvolvimento de software, no entanto, ela não é exclusiva das metodologias ágeis [B+ 01], ela é uma prática de desenvolvimento independente que pode ser empregada por qualquer equipe. No caso dos métodos e arcabouços ágeis que abordam aspectos mais gerenciais, como Lean [Ohn98][PP03] e Scrum [SB01], a prática de testes automatizados não é sugerida explicitamente, mas eles pregam a qualidade disseminada por toda equipe e a todo momento, que é uma abordagem completamente compatível com os objetivos dos testes automatizados e dos métodos ágeis que se concentram em aspectos mais técnicos, desta forma, é natural que estas metodologias trabalhem em conjunto com outras que se concentram mais em atividades mais técnicas, como a Programação eXtrema [BA04]. 2.4.1 Programação eXtrema A Programação eXtrema, também conhecida como XP (de Extreme Programming), foi criada por Kent Beck em 1996, o mesmo criador do arcabouço de testes SUnit [Bec94], que serviu de referência para muitos outros arcabouços de testes automatizados. XP surgiu de um desafio de reverter a situação de um projeto de folha de pagamento da empresa Chrysler, o qual já havia estourado os limites de custos e prazos. Para isso, o projeto adotou uma nova metodologia, a qual aplicaram ao extremo um conjunto de práticas recomendadas de programação [BF01][AAT98] com disciplina e organização. Devido ao sucesso do projeto, Kent Beck aglutinou os méritos da metodologia e a oficializou como Programação eXtrema, através do primeiro livro de XP [Bec99]. Dentre as práticas recomendadas por XP está Testes Automatizados [CH02], que está diretamente relacionada com algumas das outras práticas da metodologia. Por isso, para aplicar XP com sucesso é fundamental o emprego de testes automatizados de qualidade. Abaixo segue uma lista das práticas mais relacionadas com testes automatizados: • Refatoração: É um artifício poderoso para aprimorar o design da aplicação e melhorar a legibilidade e clareza do código, mas é uma tarefa arriscada de aplicar em sistemas que estão seguramente 7 estáveis, pois não há garantias que a tentativa de melhoria pode trazer novos problemas. Por isso, para fazer refatorações com segurança, é necessário uma boa bateria de testes automatizados, que certificam quais comportamentos foram modificados indevidamente. • Código Compartilhado: Atualmente, são raros os projetos que não compartilham seus códigos. A segurança que os desenvolvedores tem para alterar quaisquer trechos do sistema, mesmo os que não foram iniciados por eles, vem de uma boa bateria de testes automatizados. • Integração Contínua: Alterações freqüentes do código fonte, da arquitetura e configurações de arcabouços, também possibilitam maior confiabilidade com testes, que garantem que as otimizações e melhorias não alteraram o comportamento. • Design incremental: É uma das práticas mais conflitantes com as metodologias tradicionais, que costumam seguir o modelo de cascata [Pre04]. Ela incentiva a não planejar todo o esqueleto da aplicação de uma só vez, e desta forma, sugere que o design seja modificado gradativamente. Testes servem para se certificar que apesar das alterações no design, o comportamento final das funcionalidades não foram alterados. Algumas técnicas de escrita de testes como TDD e BDD influenciam diretamente o design da aplicação. • Entregas Freqüentes: Testes trarão qualidade e segurança para que a equipe mostre freqüentemente para o cliente o produto e distribua releases com freqüência. • Tracking: Existem muitas métricas que auxiliam o gerenciamento dos testes automatizados, sendo que muitas delas estão diretamente ligadas com o sucesso do projeto. Mais detalhes no capítulo 11 • Envolvimento real com o Cliente: Esta prática se relaciona com testes automatizados quando são utilizados testes de aceitação, que podem servir de excelente forma de criação de metáforas que facilitem o entendimento dos requisitos e histórias pelso desenvolvedores. 2.5 Software Livre O movimento de Software Livre juntamente com a internet explicitou a natureza e o potencial das redes colaborativas [Ben01][Ben06], que possibilitam a criação de sistemas imensos como o Linux1 e com uma quantidade enorme de dados como a Wikipedia2 . Hoje existem diversas frentes de incentivo e estudo de Software Livre que visam aumentar o uso de todo seu potencial, além de visar formas de melhorias com as contribuções e com o gerenciamento dos projetos. O projeto QualiPSo [Qua] faz parte de uma destas frentes, que possui como um de seus objetivos, estudar formas para aumentar a qualidade dos sistemas de software livres, como por exemplo, através de testes automatizados. Para os criadores de projetos livres, o resultado esperado é que o projeto tenha uma boa repercusão e que tenha contribuições de qualidade. Para o primeiro resultado esperado, a qualidade do sistema é essencial e pode ser conseguida através de testes automatizados ou de outra prática que está no controle e domínio do criador. No entanto, os criadores não tem controle da qualidade das contribuições que o projeto recebe, desta forma, cada contribuição precisa de um estudo detalhado para certificar que ela está agregando algum valor ao sistema e não está adicionando defeitos em outros módulos do projeto. Esta falta de agilidade pode ser sanada com ajuda de testes automatizados, que servem de documentação além de trazer segurança para modificações do código, pois permitem avaliar se erros de regressão foram adicionados a qualquer momento. 1 http://www.linux.org 2 http://wikipedia.org 8 Esta insegurança é recíproca, os colaboradores também tem como objetivo obter boa repercusão nas suas contribuições, além de querer que o projeto evolua para que seus sistemas também sejam aperfeiçoados. Além disso, se o colaborador evidencia aos mantenedores do projeto que sua contribuição é de qualidade, então a chance dela ser aglutinada ao projeto aumenta. Por fim, a prática de testes automatizados traz benefícios para todas as pessoas envolvidas com o software livre, inclusive os usuários que obtém um produto de melhor qualidade, além de ganharem com a velocidade e menos burocracia nas melhorias do projeto. 2.6 Definições de Termos e Siglas O tema de testes de software possui diversas frentes de estudo, vindas de comunidades ágeis, comunidades tradicionais e de controle de qualidade. Este trabalho utiliza principalmente os termos utilizados pelas comunidades ágeis, então abaixo segue uma lista de termos bastante utilizados por outras comunidades relacionados com os termos do trabalho para facilitar a leitura. Defeito/Engano/Erro/Falha: Existem padrões que diferenciam estes termos [Pre90], mas este trabalho não se preocupa em distingüir estes termos com precisão. De qualquer modo, alguns arcabouços de testes automatizados diferenciam falhas de erros, onde falha é quando o valor esperado em um teste não é obtido, e erro é quando ocorre algum problema com a execução do teste. Verificação: Atividade que verifica se o produto está sendo desenvolvimento corretamente. A maioria dos tipos de testes são de verificação, tais como os testes de unidade, de interface, de sanidade entre outros. Validação: Atividade que verifica se o produto que está sendo desenvolvido é o esperado, isto é, se atende aos requisitos. Esta é a responsabilidade dos testes de aceitação, vide capítulo 9. Inspeção: É um processo de revisão formal dos requisitos para capturar erros antes da implementação. Quando falamos em métodos ágeis, a inspeção é dada através da comunicação direta com o cliente para esclarescer dúvidas em relação às histórias. A área de testes de software, assim como muitas áreas da computação, possui diversas siglas, sendo que muitas delas possuem o mesmo significado, por isso também é importante conhece-las para evitar má interpretação. Algumas dos termos serão mais aprofundados pelo trabalho enquanto outros só serão citados. SUT System Under Test, ou Sistema sob Teste: É a aplicação que está sendo exercitada pelos testes automatizados. AUT Application Under Test, ou Aplicação sob Teste: Mesmo que SUT. TFD Test First Development, ou Testes a priori: Técnica de escrever testes automatizados antes da implementação. TAD Test After Development, ou Testes depois da implementação: Técnica de escrever testes automatizados depois da implementação. POUT Plain Old Unit Testing, ou Maneira Antiga de Escrever Testes de Unidade: Modo mais radical que a comunidade de TDD utiliza descrever TFD. TDD Test-Driven Development, ou Desenvolvimento Dirigido por Testes: Técnica de escrever testes baseada em um iterações curtas de um ciclo de escrita de testes, escrita de implementação e refatoração do design. 9 BDD Behavior-Driven Development, ou Desenvolvimento Dirigido por Comportamento: Mesmo que TDD mas com um passo amplo a mais, que é a descrição do comportamento que aponta o ponto de partida para a escrita dos testes. EDD Example-Driven Development, ou Desenvolvimento Dirigido por Exemplos: Mesmo que BDD. STDD Story Test-Driven Development, ou Desenvolvimento Dirigido por Testes da História: Mesmo que BDD. TDDD Test-Driven Database Design, ou Modelagem do Banco de Dados Dirigido por Testes. A idéia é a aplicar as práticas de TDD na criação de um schema de banco de dados [Amb06]. Em algumas das definições foram citados alguns tipos de testes, o capítulo seguinte irá definir cada um deles. 10 Capítulo 3 Tipos de Testes Automatizados Existe uma infinidade de tipos de erros que podem acontecer em um sistema de software, desde enganos simples por desatenção até mal entendimento dos requisitos. Portanto, é natural que exista um ampla gama de tipos de testes de software, os quais possuem objetivos particulares para buscar falhas específicas. Vale ressaltar que alguns tipos de testes só fazem sentido com o auxílio da automação devido a sua complexidade de implementar ou executar os testes. Uma erro comum é exercitar ou utilizar uma ferramenta de um tipo de teste específico para objetivos que não são de sua responsabilidade natural, o que acarreta em muitos anti-padrões e portanto testes de má qualidade. Portanto, é necessário conhecer o leque de opções para optar pelos tipos mais apropriados para diferentes contextos. 3.1 Teste de Unidade O teste de unidade é responsável por testar os menores trechos de código de um sistema que possui um comportamento definido e nomeado de entrada e saída, podendo ser funções e procedimentos para linguagens procedurais e métodos em linguagens orientadas a objetos. 3.2 Teste de Integração Testa a integração de unidades e arcabouços. Qualquer teste que não é isolado de outras dependências pode ser entendido como um teste de integração, isto é, um teste que pode identificar erros da comunicação entre módulos do sistema, por exemplo, um teste de desempenho pode falhar devido a um erro no sistema. No entanto, vale ressaltar que para buscar erros de integração é recomendado que uma bateria especializada deste tipo de teste seja criada, ou seja, uma bateria exclusivamente para buscar erros de integração, sem outras finalidades. 3.3 Teste de Interface de Usuário São testes que buscam erros de implementação da interface de usuário, como Graphical User Interface (GUI) ou Web User Interface (WUI). Esta camada de um sistema merece atenção especial porque ela é responsável por lidar com a infinidade de combinações de eventos do usuário e conectar as funcionalidades apropriadas do sistema, além de apresentar os dados corretamente. 11 3.3.1 Teste de Layout São testes que verificam se as interfaces de usuário estão sendo desenhadas corretamente e da forma esperada. Devido a carência de ferramentas e estudo e também da subjetividade da verificação, este tipo de teste é difícil de automatizar. Uma alternativa é testar o layout produzido através de chamadas diretas das funções que renderizam a aplicação. Outra abordagem é automatizar screenshots das telas, e então a análise é feita manualmente. 3.4 Teste de Aceitação São testes idealmente especificados por clientes ou usuário finais do sistema que definem os comportamentos esperados pelo sistema. Este tipo de teste verifica se o sistema atende ao comportamento desejado, o que o torna útil para identificar quando o projeto foi finalizado, já que quando o sistema passa em todos os testes de aceitação, então o sistema foi concluído [MC05]. 3.5 Teste de Sanidade (Sanity Tests) Testes de sanidade são testes pouco específicos que buscam encontrar erros grandes de algoritmos que possuem uma infinidade de casos possíveis de testes. Os testes verificam se os resultados esperados não estão fora de um intervalo esperado, ou então verificam que os resultados obtidos atendem a certas propriedades relacionadas ao algoritmo. 3.6 Teste Fumaça (Smoke Tests) Testes fumaça são simples, rápidos e muito abrangentes, mas pouco específicos. Ele é útil para testes pós-produção, como verificar erros de ambiente, configuração e permissão. 3.7 Teste de Sistema São testes realizados após a implantação do sistema em um ambiente idêntico ao de produção, incluindo o hardware, sistema operacional e outras dependências, visando buscar limitações de carga e incompatibilidade dos softwares. Últimas Considerações A automação de cada tipo de teste possui um conjunto de benefícios e um grau de dificuldade específico, então cada projeto precisa avaliar o custo-benefício para tomar as decisões corretas, já que é normal ter que priorizar e optar por tarefas específicas em um projeto. Outro ponto a avaliar é o conhecimento do time em automatizar determinado tipo de teste, pois a automação pode trazer mais problemas caso os testes não sejam bem implementados [Mar98], por isso é importante saber como escrever bons testes automatizados, como é discutido na parte II deste trabalho. 12 Parte II Práticas, Padrões e Técnicas (ESBOÇO) 13 Capítulo 4 Bons Testes Automatizados (ESBOÇO) Como citado na filosofia dos testes automatizados, os testes podem ajudar muitas coisas, mas tambem eles podem se tornam uma nova fonte de problema, como gargalos no desenvolvimento e perda de prazos. Tudo por causa de testes mal implementados e anti-padroes rotineiros. Por isso é importante seu estudo, para que a possibilidade do beneficio nao se torne um desastre. 4.1 Filosofia da Automação de Testes Devido à grande evolução e estudo da prática de testes automatizados, hoje a automação de testes possui muitas técnicas e atribuições com responsabilidades discrepantes, abordando desde temas técnicos como características do código fonte, passando por especialidades de qualidade de software até regras de negócio seguindo o linguajar do cliente. Por essa razão, o primeiro passo para o estudo, aprendizado e aperfeiçoamento do tema é compreender seus objetivos e preocupações. Esta seção irá abordar temas como: Quando e como implementar os testes e Quando e como executar os testes. 4.2 Princípios Básicos Aqui serão discutidos os princípcios básicos para testes automatizados de qualidade, como: Convenções, Simplicidade, Ambiente de Testes, Independência, Suite de testes, Executar os testes toda hora, Bom desempenho. Também serão descritas as principais qualidades de um bom teste: Isolado, Automatizado, Rápida implementação, Rápida execução, Único. [Kan97] 4.3 Cheiros Nesta seção serão descritos alguns dos cheiros mais comuns de refatoração dos testes, como: Testes Obscuros, Complexos, Módulos difícil de testar, Replicação do código de testes e Lógica dos testes em produção. Também serão descritos cheiros de comportamento, como: Testes frágeis, Necessidade de intervenções manuais e testes lentos. 4.4 Padrões Padrões gerais de desenvolvimento. 15 4.5 Anti-Padrões Anti-padrões gerais de desenvolvimento. 16 Capítulo 5 Padrões de Automação (ESBOÇO) Padrões, anti-padrões e cheiros em testes automatizados. 5.1 Objetos Dublês [Mes07] 5.1.1 Introdução 5.1.2 Benefícios Velocidade, isolamento, prática no desenvolvimento. 5.1.3 Tipos De Dublês • Dummy Object • Test Stub • Test Spy • Mock Object • Fake Object 5.1.4 Anti-Padrões Focar o mock à implementação, não ao comportamento. Devido a flexibilidade dos arcabouços, é fácil cometer estes erros. 5.2 Expressão Regular Cada caracter adicional ou ausente pode alterar completamente o comportamento esperado. Ferramentas facilitam bastante testes manuais para ajudar a criação das expressões. 5.3 Listas Vazias, nulas, cheia, com objetos não esperados. 17 5.4 Strings Vazias, nulas, compridas, strings de usuário, validação, conversão para números, datas, tipos específicos. 5.5 Algoritmos Matemáticos Testes de sanidade. Algoritmos prontos. Limites das variáveis. Algoritmos prontos? Otimização? 5.6 Algoritmos Aleatórios Testes aleatórios? Estatísticos? Testar separadamente a ação da aleatoriedade? 5.7 Processos assíncronos Pausas? Ou esperas? 5.8 Objetos • Classes privadas, protegidas, públicas, abstratas, internas e anônimas. • Métodos privados, protegidos, públicos, de instância e de classe. • Pacotes, padrões, singleton. 5.9 Aspectos Como evitar loops, recursão infinita? Assim como expressão regular, um caractere errado pode adicionar ou remover pointcuts importantes. IDEs ajudam, mas como vasculhar se os pointcuts estão corretos em todas as classes do programa? 5.9.1 Aspecto que interfere no outro: como habilitar e desabilitar em tempo de execućão? Idéia é criar protótipos das classes que atendem e não atendem os pointcuts e ir fazendo os testes. TDD leva a isso: não precisa criar uma classe real do sistema, cria a classe que você espera. • Protótipos de classes que atendem e que não atendem os pointcuts. • Testar o comportamento adicionado, dado um protótipo que atende o pointcut. 5.10 Web Services Obter entradas e saídas. Testar mockado. Testar integrado pode simular lentidão do sistema. 5.11 Reflexão Adicionar protótipos que atendem e não atendem os requisitos a serem encontrados pela reflexão. Lentidão e diversas possibilidade de erro, como permissão, segurança e depencências. 18 Capítulo 6 Testes de Unidade (ESBOÇO) Tipo mais fácil de escrever e o que detecta maior quantidade de erros. Quando é identificado um erro, é rápido a localização da sua causa. 6.1 Preparação do Ambiente Explicar conceitos comuns, como Set up, Tear down. 6.2 Testes clássicos vs Testes completamente isolados Testando a lógica de objetos independentemente ou fazendo testes integrados? Discussão sobre o uso extensívo de mocks. 19 20 Capítulo 7 Testes de Banco de Dados (ESBOÇO) Padrões, bancos de dados em memória, bancos de dados populados, fixtures. Sequences. Rollback. Problemas de concorrência: Recurso único para testes concorrentes. Transaction Rollback Teardown. Table Truncation Teardown. Concorrência: Database Sandbox. [AJ02] 7.1 Arcabouços Objeto-Relacional Mapeamento pode estar sendo feito do modo não esperado. Mal uso do arcabouço. O arcabouço pode ter defeitos para o banco de dados específico. Pode funcionar corretamente, mas fazer unindo tabelas que contém uma grande quantidade de dados. Pode identificar pontos de otimização. Opções de cascata, deleção lógica. 7.2 Stored Procedures e Triggers Pode-se fazer testes na linguagem que chama a procedure, sem focar na implementação. 7.3 Replicação de Dados Evento assíncrono: Problemas de lentidão da replicação, banco de dados inconsistentes. 21 22 Capítulo 8 Testes de Interface de Usuário Interface de usuário (UI) é o módulo de um programa que faz a comunicação entre o usuário e as camadas internas de um programa através de comandos iniciados a partir de comandos de texto ou componentes visuais, como botões ou tabelas. Ela recebe informações e eventos do usuário, tais como parâmetros e cliques do mouse ou teclas pressionadas, e interage com outros módulos do programa através de troca de mensagens ou chamada de métodos. As GUIs (Graphical User Interface) em especial, tornam os programas fáceis de serem utilizados, se comparados com aplicações modo texto, já que são mais intuitivas e dispensam muitos manuais de instruções. Por isso, elas são módulos fundamentais para os muitos projetos, como para aqueles que o sucesso depende mais da interação homem-computador do que de algoritmos e arquiteturas elaboradas. Hoje em dia, os programas se preocupam cada vez mais com a aparência, agilidade e facilidade de uso. Para alcançar esses quesitos e garantir a qualidade desses softwares, é imprescindível que as interfaces sejam bem implementadas. Em muitos sistemas, a porção de código dedicada às interfaces constitui mais da metade de todo o sistema. Além disso, as GUIs precisam lidar com todos os eventos inusitados que um usuário pode criar e, portanto, elas formam uma camada muito suscetível a falhas. Para minimizar o número de erros, é necessário muitos testes. O modo popular dos desenvolvedores testarem as telas é manualmente após o término do desenvolvimento, que possui muitos malefícios. Um que é muito claro é a falta de praticidade e a lentidão deste método, porque em muitos casos temos que navegar por diversas telas do sistema até chegar na qual queremos testar. Portanto precisamos de um método efetivo e rápido, logo, testes automatizados. 8.1 Responsabilidades da UI Um projeto mal arquiteturado não separa as regras de negócio do código da interface de usuário. Uma arquitetura recomendada que facilita esta separação é a MVC (Model-View-Controller) [FB96]. Seguindo esta arquitetura, a UI pode ser responsável apenas pela exibição dos dados (camada da visão) e pelo tratamento de comandos (camada do controlador). Estas duas camadas exigem muitos cuidados e podem possuir muitas falhas de implementação. Abaixo segue uma lista de responsabilidades do código da camada da visão: • Composição da Interface: É preciso criar, configurar e adicionar os componentes que irão compor uma interface. Componentes mal configurados, como botões desabilitados ou componentes invisíveis, podem impor um estado inconsistente à interface; • Atualização da Tela: A interface precisa estar sempre coerente com o estado dos dados, para isso elas tem que ser atualizadas a cada modificação dos dados. Essa característica é uma grande fonte de erros para as interfaces; 23 • Internacionalização1 : É um módulo associado a camada de visão que define a linguagem que será exibida nos textos da interface; • Layout: A aparência e agilidade de um programa depende da GUI, por isso seu código contém configurações de layout. Alterações de layout, apesar de serem aparentemente simples, podem criar erros de regressão. Agora em relação a camada do controlador: • Tratamento de Eventos do usuário: O controlador recebe comandos e dados do usuário e os encaminha para as camadas de base, no entanto, os dados nem sempre vêm da forma que estas camadas esperam e os comandos podem ser recebidos em uma ordem inapropriada. Por isso, o controlador precisa validar os dados e tratar as situações inusitadas; • Tratamento do conteúdo e mensagens das camada de base: Uma chamada às camadas de base de um sistema pode retornar um conjunto de dados ou uma mensagem de sucesso ou de erro de regras de negócio. O controlador precisa encaminhar estas informações para a camada de visão de maneira apropriada, além de reconhecer e tratar possíveis erros das camadas de base. Devido a diversidade de responsabilidades, as interfaces de usuário apresentam muitos pontos sucetíveis a erros, portanto é recomendado uma bateria de testes automatizados. Como este é um tipo especial de teste, com características bem particulares, há algumas formas alternativas de automação, como é discutido a seguir. 8.2 Paradigmas de Automação Testar uma interface gráfica é difícil e diferente, principalmente para quem está acostumado a escrever apenas testes de unidade. As GUIs são criadas para interagirem com usuários, não com outros programas, por isso, as funcionalidades públicas que devem ser testadas são as que a própria GUI disponibiliza, e não simplesmente métodos, como no caso das classes. Testes automatizados de interface não são tão populares quanto os testes de unidade. Isso devese não só a sua alta complexidade, mas também por causa da falta de boas ferramentas, bibliotecas e arcabouços que facilitem esta tarefa. Mas esta realidade está mudando, hoje existem diversas opções que tornam o desenvolvimento muito simples e rápido. As diferentes ferramentas e técnicas utilizam diferentes estilos de testes, como testes de caixa branca, caixa preta ou uma mescla dos dois estilos. No entanto, o raciocínio para criar testes de interface é sempre o mesmo: simular uma ação de usuário e verificar o estado da interface após a execução do comando, comparando com o estado esperado. Transformando isso em pseudo-algoritmo, temos: 1. Primeiro, a partir de técnicas de localização de elementos da interface gráfica, obtemos as referências dos componentes da interface que o usuário simulado vai interagir; 2. Depois temos que criar ou simular um evento com seus atributos devidamente configurados, para depois ativá-lo no elemento desejado; 3. Por último, localizamos os elementos que esperam ser alterados pelo comando e comparamos seu estado com o esperado. 1 Internacionalização, que também é conhecido pela sigla i18n, é o processo de tornar um software independente do seu idioma. 24 Os meios para se encontrar um elemento numa interface variam de acordo com o arcabouço utilizado para implementar a interface, mas os modos mais comuns são através do tipo de componente e de seus atributos ou pela ordem que ele foi adicionado na tela. Para simular eventos, as ferramentas disponibilizam métodos que abstraem todo o módulo interno da interface. Por exemplo, métodos "click"simulam cliques do mouse em botões ocultando movimentações do mouse e criação dos objetos que guardam os atributos do evento. Quando utilizamos um gravador de interação, todo este processo é feito automaticamente. Entretanto, ao criar os testes programaticamente com ou sem o auxílio de uma biblioteca, precisamos repetir estes três passos para cada teste. A seguir vamos explorar estes métodos. Gravadores de Interação Gravador de Interação é um programa que executa a aplicação a ser testada e grava informações do estado da interface permitindo a comparação com estados esperados. Para implementar os testes, os gravadores utilizam uma das duas estratégias: uma antiquada é a utilização de screenshots das telas. O gravador salva uma imagem da interface após uma seqüência de comandos e compara pixel2 a pixel com uma imagem do estado esperado; Outra estratégia, que é mais interessante, é a utilização de scripts ou linguagens de alto nível que representam as ações e eventos criados pelo usuário, e as comparações são feitas por valores de variáveis. A primeira estratégia é péssima porque é lenta e nada prática para lidar com alterações na interface, por isso, neste texto vamos nos concentrar apenas na segunda estratégia. Conclusões Podemos perceber que todas as opções tem vantagens e desvantagens consideráveis, por isso temos que analisar cada situação individualmente e também quais ferramentas e bibliotecas que podem ser usadas com a aplicação a ser testada. Mas independentemente das ferramentas, podemos fazer algumas conclusões. A opção mais crítica é programar os testes sem auxílio de bibliotecas, pois precisamos de mais conhecimento do arcabouço Swing para implementar os testes do que para implementar a própria interface. Este método só deve ser escolhido se não existir ferramentas e bibliotecas que suportem a linguagem e arcabouço da interface que a aplicação sob teste utiliza, e também, se os desenvolvedores não terão muitas dificuldades em escrever os testes. Por exemplo, se foram eles que implementaram a interface, eles já sabem como funciona a criação de eventos e portanto não terão grandes contratempos. A gravação de testes é o método mais amigável, mas sua escolha depende exclusivamente da qualidade do gravador, isto é, do script que ele gera. Se ele possuir um sistema que permite a modularização de testes e também não possui incompatibilidade com os componentes da sua aplicação, então este método é o ideal para as aplicações que já estão prontas e não possuem testes, mas precisam de correções, refatorações e adição de novas funcionalidades. Antes de iniciar este processo de melhoria, são feitos testes para evitar refatorações incorretas e para evitar erros de regressão. Outra situação válida são aquelas que precisam de testes a curto prazo e os testes em breve poderão ser descartados, então pode-se ignorar a replicação de código. Já em relação a incompatibilidade, deve ser avaliado cada caso em particular. Se o componente for uma exceção do seu programa e em um trecho onde não há necessidades de testes a curto prazo, então pode-se utilizar o gravador, ao passo que, se for um componente importante que é incompatível, então de nada servirá os poucos testes que esta ferramenta conseguirá fazer. Uma outra solução para os softwares que já estão implementados, é combinar a programação e a gravação de testes, mas este caso funciona apenas quando os gravadores conseguem devolver um script na mesma linguagem dos testes. Podemos gravar uma seqüência de ações e asserções apenas para gerar 2 Pixel é o menor ponto que forma uma imagem digital, portanto, um conjunto de pixels forma uma imagem inteira. 25 um script provisório, que será aglutinado e adaptado pelos testes programados. Este script servirá de base para montar testes mais elaborados ou de esqueleto para agilizar a criação dos testes programáticos. Desta forma conseguimos agilidade na criação dos testes e também total controle quando a qualidade do código. Uma última consideração é para quando queremos fazer testes a priori [Ast03][Bec02], que é incompatível com os gravadores de interação, por isso, a única opção é implementá-los programaticamente. 26 Capítulo 9 Testes de Aceitação (ESBOÇO) Alguns pontos a serem discutidos nesta seção: Clientes podem especificar os testes enquanto os desenvolvedores os implementam. Clientes podem gravar os testes a partir da interface de usuário. Precisa da colaboração do cliente. 27 28 Capítulo 10 Técnicas de Escrita de Testes Automatizados (ESBOÇO) TAD, TFD, TDD, BDD... São as técnicas conhecidas para escrita de testes automatizados. 10.1 Testes depois da Implementação (TAD) Vale a pena testar? Está funcionando! Terei que alterar meu código que está funcionando para testar? Meu código não é testável. 10.2 Testes antes da Implementação (TFD) Força o uso de bons princípios de design. SOLID: coleção de boas práticas de design de orientação a objetos para alcançar objetivos como baixo acoplamento, alta manutentabilidade (TODO), localização intuitiva do código interessante, etc. SRP: Single Responsibility Uma razão para existir, uma razão para mudar. OCP: Open Closed Principle Aberto para extensões e fechado para alterações. LSP: Liskov Substitution Principle Objetos semanticamente substituíveis por suas classes base ou interfaces. ISP: Interface Segregation Principle Não force clientes a depender de uma interface, ele não precisa nem saber. DIP: Dependency Inversion Principle Dependa de abstrações, não de informações concretas ou implementações. Força a injeção de dependência e induz o código a ser testável. 10.3 Desenvolvimento Dirigido por Testes (TDD) 10.3.1 Introdução TFD + Refatoração? TFD não existe disciplina de quando escrever os testes. TDD é sobre design, não sobre testes. [Bec02] [Ast03] [LC04] 29 10.3.2 Refatoração Fala de refatoração [Fow99], que é é fundamental para TDD. 10.3.3 Fluxo Vermelho: Testes falhando Verde: Testes passando Refatoração: Limpar código e testes 10.4 Desenvolvimento Dirigido por Comportamento (BDD) Padrão de histórias: As a... I want... so that... Dado... Quando... Então: Pré-requisitos Explicar o seu nascimento. Tanto a linguagem como o por onde começar o seu TDD. É um ponto de partida. Histórias: SMART DRY? Don’t repeat yourself. Vale a pena? Ferramentas direcionadas para testes de código não estão na linguagem do negócio e não são adequadas para diversos cenários de teste. SMART: Specific, Measurable, Achivable, Relevant, Time-Boxed. Figura comparando com TDD. DDD 30 Parte III Adotando e Gerenciando Testes Automatizados 31 Capítulo 11 Métricas Antes de entrar no assunto de métricas, é preciso informar a terminologia utilizada por este trabalho: Uma medida é uma avaliação em relação a um padrão definido, por exemplo, um sistema tem 500 linhas de código. Já uma métrica é um método que utiliza uma ou mais medidas para definir se um sistema, componente ou processo possui um certo atributo. Por exemplo, para saber se um sistema é grande (atributo) podemos utilizar a medida de linhas de código [Sat07][SGK07]. Como toda medida representa um atributo, este trabalho interpretará as informações de estudo simplesmente como métricas. Seguindo a terminologia, as métricas exercem um papel fundamental no gerenciamento de tarefas e projetos, mas elas precisam ser bem empregadas. As métricas podem ser utilizadas para conhecer certos aspectos de um trabalho e estabelescer objetivos de um projeto, como sugere o processo PDCA (Plan -> Do -> Check -> Act, ou Planeje -> Faça -> Estude -> Aja) [She39], ou ainda, se os objetivos estão definidos, pode-se utilizar a abordagem GQM (Goal -> Question -> Metrics, ou Objetivo -> Pergunta -> Métricas)[VRBR96], desta forma, as métricas só são coletadas no momento oportuno. Os cheiros de código (code smells) podem servir de ponto de partida para coletar métricas para estudo ou definir objetivos de melhorias. Dado esta introdução, a seção seguinte apresentará algumas métricas que podem ser úteis para guiar planejamentos e alcançar objetivos comuns tanto de equipes que estão começando a aplicar testes automatizados como também das que pretendem melhorar seu processo de desenvolvimento. 11.1 Métricas para Testes Automatizados Toda metodologia de desenvolvimento de software busca respeitar acordos definidos com o cliente, sejam acordos de prazos, qualidade, escopo ou dinheiro. No caso das metodologias que integram testes automatizados como parte do desenvolvimento, em especial a Programação eXtrema que recomenda testes automatizados como uma de suas práticas primárias, é comum que equipes precisem de um gerenciamento especial para administrar os testes, portanto, é comum a coleta de métricas relacionadas a testes automatizados. Além disso, pelos testes influenciarem diretamente a qualidade e progresso de um projeto, um bom conjunto de métricas relacionadas à testes automatizados podem elucidar o estado do projeto e ajudar a estipular metas. A seguir, segue uma lista de métricas de acompanhamento de testes automatizados: 1. Testabilidade: Avalia se o código do sistema a ser testado permite a escrita de bons testes automatizados, através da avaliação de boas práticas de programação como injeção de dependência e práticas não recomendadas como variáveis globais (públicas e Singletons). Para projetos com testes automatizados, esta métrica ajuda a identificar módulos do sistema que precisam ser refatorados. Para projetos que estão começando a automatizar seus testes, esta métrica pode ser útil para analisar riscos e estabelescer quais pontos são mais e menos críticos para se testar. Também 33 é um bom indicador para saber se o projeto foi escrito com testes a priori ou TDD, que favorece escrever código testável. 2. Cobertura: Recupera quais trechos do sistema sob teste foram e quais não foram exercitados pelos testes. Note que um trecho exercitado não significa que ele está livre de defeitos ou que foi testado. A única certeza é que os trechos não exercitados não foram testados, portanto tem maior chance de possuir defeitos. Esta métrica é especialmente útil para projetos que já possuem uma bateria de testes considerável, ou então para avaliar se um projeto foi escrito utilizando TDD, que propicia a uma alta cobertura do código. 3. Fator de Teste: É o total de linhas dos testes por total de linhas do sistema. Ela é útil para comparar módulos de um mesmo projeto e ajudar a determinar quais são os módulos mais testados e quais precisam de maior atenção. Esta métrica não é recomendada para o acompanhamento da evolução dos testes de um projeto, pois a lógica do código dos testes não possui qualquer relação com a lógica do código do sistema. 4. Número de testes por linha de código do sistema: Esta métrica pode ser útil para acompanhar a evolução dos testes automatizados de um projeto, dado que o sistema cresce sem alterações drásticas quanto a sua complexidade. 5. Número de linhas de testes: Análogo ao número de linhas de um sistema, esta métrica dá uma pequena dimensão do código dos testes, que pode ser utilizada para o planejamento de tarefas de estudo e manutenção do código. A avaliação desta métrica em uma amostra do código como classes ou métodos, pode identificar testes que precisam de refatoração. 6. Número de testes: Métrica para acompanhar a evolução do desenvolvimento de testes, útil principalmente em projetos que estão começando a ter testes automatizados. 7. Número de asserções: É uma métrica que ajuda a detectar se os testes estão realmente testando o sistema. 8. Número de testes pendentes: É comum escrever testes marcados como pendentes (se o arcabouço de teste permitir) que serão implementados no momento apropriado. Se o acompanhamento da quantidade destes testes ao longo de uma iteração indicar que o número de pendentes não está diminuindo, pode ser um sinal que os prazos estão curtos e que os testes estão sendo sacrificados. 9. Número de testes falhando: Esta métrica pode ser útil para detectar a fragilidade dos testes, além de servir de acompanhamento para o conserto destes testes. 10. Número de asserções por método: Pode indicar métodos que precisam ser refatorados caso o número seja alto, pois podem ser responsáveis por testar mais de uma idéia. 11. Replicação de código dos testes: Identifica trechos do código dos testes que precisam ser refatorados, mas também pode indicar que o código do sistema também possui replicação de código. 12. Quantidade de defeitos encontrados: Indicar a qualidade do sistema e também pode indicar a falta de testes automatizados. 13. Tempo de execução da bateria dos testes: Métrica para determinar se o programa possui gargalos ou para determinar se os testes precisam ser refatorados. Vale ressaltar que os valores obtidos das métricas estão diretamente relacionadas ao contexto do projeto e do sistema, pois elas dependem de muitos fatores como a linguagem de programação e complexidade do produto. Desta forma, é inviável utilizá-las isoladamente para definir o estado de um projeto ou mesmo para comparar projetos distintos. 34 Para estes fins, sempre é necessário uma análise que fará a interpretação das informações, principalmente porque os resultados podem apresentar valores que não são esperados para a realidade do projeto, isto porque as métricas podem ser facilmente burladas, propositalmente ou por falta de experiência com o desenvolvimento dos testes, como demonstram os exemplos a seguir: • Exemplo 1: Um número alto de testes e fator de teste pode aparentar que um sistema possui poucos defeitos já que passa a impressão que foram verificados diferentes cenários de testes, mas também pode apresentar que o código do sistema possui uma grande replicação de código, e portanto os testes também são replicados. • Exemplo 2: Uma alta cobertura do código pode significar que o sistema está muito bem testado, já que não há trechos de código esquecidos pelo código, mas também pode mostrar que o sistema foi muito mal testado, caso os testes não possuam verificações, apenas chamadas dos métodos do sistema. Estas métricas relacionadas a testes automatizados ajudam a estabelescer objetivos de melhoria da qualidade e de produtividade da automação dos testes, dentre outros objetivos que são comuns a diversos projetos. A tabela 11.1 aponta alguns destes objetivos e as métricas mais recomendadas para ajudar no gerenciamento. ``` ``Métrica Objetivo `` Encontrar defeitos Melhorar o código do sistema Melhorar o código dos testes Introduzir testes automatizados em novos projetos Introduzir testes automatizados em sistemas legados Acompanhar a automação dos testes 1 2 3 o o o o o 4 o 5 6 o 7 8 9 10 11 12 13 o o o o o o o o o o o o o o o o o o o Tabela 11.1: Objetivo vs Métrica (Goal vs Metric) É importante lembrar que outras métricas podem ser geradas a partir da combinação de uma ou mais destas métricas, pois cada uma delas utiliza uma medida diferente. Quanto mais evidências, mais fácil é a análise dos dados e também a definição de estratégias, desde que não tenha perda na agilidade do processo e que o excesso de informações não tire o foco do que é realmente necessário melhorar. 35 36 Capítulo 12 Estratégias para Automação de Testes (ESBOÇO) Por onde comećar, aspectos importantes, etc. 12.1 Por onde começar Testes não subistitui conhecimento de lógica de programação, orientação a objetos ou padrões de boas práticas de programação, apesar de influenciar positivamente. Estudos, cursos, dojos. 12.2 Baterias de Testes Aqui irei enfatizar da importância de ter baterias isoladas para cada tipo de teste. Discutir estratégias ruins: Figura 12.1: Estratégia não aconselhada [WWC] Discutir boas estratégias: 12.3 Novos Projetos Aqui haverá uma discussão: O que é melhor? Começar com TDD? TFD? TAD? BDD? 12.4 Projetos Legados Quais tipos de testes utilizar? O projeto está bem modularizado? Os requisitos muito bem conhecidos? 37 Figura 12.2: Boa estratégia [WWC] 12.5 Software Livre Milhares de programadores praticando desenvolvimento distribuído em diversas regiões do mundo. Precisa de documentação. Depende de colaboração. [Wil00] 12.6 Garantia de Qualidade Equipe separada precisa trabalhar isoladamente? É vantajoso trabalhar em conjunto com o time de desenvolvimento? Pode atuar como cliente? 38 Capítulo 13 Considerações Finais Desenvolvimento de software é uma tarefa complexa que exige conhecimento técnico, organização, atenção, criatividade e também muita comunicação. É previsível que em alguns momentos do desenvolvimento em algumas das milhares de linhas de código alguns destes requisitos falhe, mas é imprevisível o momento que elas irão falhar, por isso é imprescindível que exista uma maneira fácil e ágil de executar todos os testes a qualquer momento, e isso só é viável com o auxílio de testes automatizados. A automação dos testes traz segurança para fazer alterações no código, seja por manutenção, refatoração ou até mesmo para adição de novas funcionalidades. Além disso, um teste programático permite criar testes mais elaborados e complexos, que poderão ser repetidos identicamente inúmeras vezes. Ainda, a automação aumenta a quantidade de tempo gasto com a verificação do sistema e diminui o tempo gasto com a identificação e correção de erros, pois todos os testes podem ser executados a qualquer momento e por conseqüência os erros tendem a ser encontrados mais cedo. É possível até automatizar a execução dos testes, com ferramentas que ficam constantemente verificando se um código foi alterado ou com ferramentas que obtém o código de um repositório automaticamente e rodam a bateria de testes através de um script. 13.1 Pontos para Pesquisa Esta área de pesquisa está em crescimento, existem muitas pesquisas a serem feitas, muitas ferramentas ainda não produzidas e muitas técnicas ainda não evidenciadas, desta forma, segue uma lista de propostas para estudos. Pesquisas : • Pesquisa onde 4 grupos pequenos com mesmo nível de experiência de programação irão implementar um mesmo sistema. Uma das equipes utilizará TDD, outra TFD, outra TAD e outra irá fazer apenas testes manuais. O tempo de de desenvolvimento e a qualidade do código e do produto gerado será analisado para buscar evidencias de vantagens e desvantagens de cada prática. • Pesquisa onde 2 grupos com mesmo nível de experiência de programação e de TDD irão implementar um mesmo sistema. Uma das equipes utilizará TDD e com uso exaustivo de objetos dublês, enquanto o segundo grupo irá fazer testes contendo certa integração dos módulos, e só utilizará objetos dublês para casos críticos. O tempo de desenvolvimento e a qualidade do código, dos testes e do produto gerado será analisado para buscar evidências de vantagens e desvantagens de cada prática. • Pesquisa onde 2 grupos com mesmo nível de experiência de programação e de TDD irão implementar um mesmo sistema que tenha possua um linguajar não conhecido pelas equipes. 39 Uma das equipes utilizará TDD enquanto a outra BDD. O tempo de desenvolvimento e a qualidade do código, dos testes e do produto gerado será analisado, para buscar evidências de vantagens e desvantagens de cada prática. Ferramentas : • Criar arcabouços para testes com aspectos. A partir de pointcuts, protótipos de classes podem ser geradas automaticamente para a realização dos testes. • Aperfeiçoar as ferramentas de relatórios de testes para torná-los mais legíveis e terem maior utilidade para documentação. • Aperfeiçoamento das ferramentas de gravação de testes de interface, de tal modo que facilite a criação de módulos, evitando a repetição de código. Também pode-se evidenciar os pontos que precisam ser refatorados. • Ferramentas para coleta e exibição de métricas de testes automatizados. • Estudos e ferramentas para testes de usabilidade, baseadas em heurísticas recomendadas pela área de Interação Homem-Computador. • Ferramentas para facilitar testes de layout. Elas podem detectar componentes que não estão visíveis assim como detectar irregularidades do layout. • Criar um robô que explora repositórios de código e ferramentas de administração de defeitos para se obter métricas que relacionam quantidade de defeitos com a quantidade de testes. Estudos : • Continuar a documentar padrões, anti-padrões e cheiros de refatoração. • Encontrar padrões ao se testar padrões de projetos e arquiteturais. 13.2 Trabalhos Futuros Por enquanto, apenas finalizar este trabalho. 40 Referências Bibliográficas [AAT98] Kent Beck David Bryant Marie DeArment Martin Fowler Margaret Fronczak Rich Garzaniti Dennis Gore Brian Hacker Chet Hen-drickson Ron Jeffries Doug Joppie David Kim Paul Kowalsky Debbie Mueller Tom Murasky Richard Nutter Adrian Pantea Ann Anderson, Ralph Beattie and Don Thomas. Chrysler goes to extremes, October 1998. 7 [AJ02] Scott W. Ambler and Ron Jeffries. Agile Modeling: Effective Practices for Extreme Programming and the Unified Process. Wiley, 2002. 21 [Amb06] Scott W. Ambler. Test driven database design. TASSQuarterly magazine, page 4, September 2006. Toronto Association of Systems and Software Quality. 10 [Ast03] David Astels. Test Driven Development: A Pratical Guide. Prentice Hall, 2003. 26, 29 [B+ 01] Kent Beck et al. Manifesto for Agile Software Development. //agilemanifesto.org, 2001. 5, 7 [BA04] Kent Beck and Cynthia Andres. Extreme Programming Explained: Embrace Change, 2nd Edition. Addison-Wesley, 2004. 6, 7 [Bec94] Kent Beck. Simple smalltalk testing: With patterns. 1994. 7 [Bec99] Kent Beck. Extreme Programming Explained: Embrace Change. Addison-Wesley, 1999. 7 [Bec02] Kent Beck. Test-Driven Development: By Example. Addison-Wesley, 2002. 26, 29 [Ben01] Yochai Benkler. Coase’s Penguin, or Linux and the Nature of the Firm. cs.CY/0109077, 2001. 8 [Ben06] Yochai Benkler. The Wealth of Networks: How Social Production Transforms Markets and Freedom. Yale University Press, 2006. 8 [BF01] Kent Beck and Martin Fowler. Planning Extreme Programming. Addison-Wesley, 2001. 7 [CH02] Lisa Crispin and Tip House. Testing Extreme Programming. Addison-Wesley, 2002. 7 [dS07] Alexandre Freire da Silva. Reflexões sobre o ensino de metodologias ágeis na academia, na indústria e no governo. PhD thesis, Departamento de Ciência da Computação, Instituto de Matemática e Estatística - Universidade de São Paulo, Setembro 2007. 3 [FB96] Hans Rohnert Peter Sommerlad Michael Stal Frank Buschmann, Regine Meunier. PatternOriented Software Architecture, Volume 1, A System of Patterns. Hardcover, 1996. 23 [Fil08] Dairton Luiz Bassi Filho. Experiências com desenvolvimento ágil. PhD thesis, Departamento de Ciência da Computação, Instituto de Matemática e Estatística - Universidade de São Paulo, Março 2008. 3 41 Home page: http: CoRR, [Fow99] Martin Fowler. Refactoring: Improving the Design of Existing Code. Addison-Wesley, 1999. 30 [Gue05] Eduardo Martins Guerra. Um Estudo sobre Refatoração de Código de Teste. PhD thesis, Instituto Técnológico de Aeronáutica, 2005. 4 [JCM07] Márcio Eduardo Delamaro e Mario Jino José Carlos Maldonado. Introdução ao Teste de Software. Campus, 2007. 4 [Kan97] Cem Kaner. Improving the maintainability of automated test suites. Proceedings of the Tenth International Quality Week, 1997. 15 [LC04] Kim Man Lui and Keith C.C. Chan. Test driven development and software process improvement in china. In Proceedings of the 5th International Conference on eXtreme Programming and Agile Processes in Software Engineering (XP 2004), volume 3092 of Lecture Notes on Computer Science, pages 219–222, 2004. 29 [Mar98] Brian Marick. When should a test be automated? Paper presented at Quality Week1998, 1998. 12 [MC05] Rick Mugridge and Ward Cunningham. Fit for Developing Software: Framework for Integrated Tests. Prentice Hall, 2005. 12 [Mes07] Gerard Meszaros. XUnit Test Patterns: Refactoring Test Code. Addison-Wesley, 2007. 4, 17 [Ohn98] Taiichi Ohno. Toyota Production System: Beyond Large-Scale Production. Productivity Press, 1998. 7 [PP03] Mary Poppendieck and Tom Poppendieck. Lean Software Development: An Agile Toolkit. Addison-Wesley Professional, 2003. 6, 7 [Pre90] IEEE Press. Standard 610.12. Ieee standard glossary of software engineering terminology, 1990. 9 [Pre04] Roger S. Pressman. Software Engineering: a Practitioner’s Approach. McGraw-Hill, 2004. 8 [Qua] QualiPSo. http://www.qualipso.org. 8 [Sat07] Danilo Toshiaki Sato. Uso Eficaz de Métricas em Métodos Ágeis de Desenvolvimento de Software. PhD thesis, Departamento de Ciência da Computação, Instituto de Matemática e Estatística - Universidade de São Paulo, Agosto 2007. 3, 33 [SB01] Ken Schwaber and Mike Beedle. Agile Software Development with SCRUM. Prentice Hall, 2001. 6, 7 [SC95] Mike Potel Sean Cotter. Inside Taligent Technology. Taligent Press, 1995. 7 [SGK07] Danilo Sato, Alfredo Goldman, and Fabio Kon. Tracking the Evolution of Object Oriented Quality Metrics. In Proceedings of the 8th International Conference on Extreme Programming and Agile Processes in Software Engineering (XP’2007), pages 84–92, 2007. 33 [She39] Walter Andrew Shewhart. Statistical method from the viewpoint of quality control. In Dover Publications, 1939. 33 42 [VRBR96] Gianluigi Caldiera Vitor R. Basili and H. Dieter Rombach. The goal question metric. In Encyclopedia of Software Engineering, pages 528–532, 1996. 33 [Wil00] L. A. Williams. The Colaborative Software Process. PhD thesis, University of Utah, Department of Computer Science, 2000. 38 [WWC] Patrick Wilson-Welsh and Lisa Crispin. Flipping the triangle: Paths to best, least-cost automated testing. In Proceedings of Agile 2008 Conference. ix, 37, 38 43