Métodos Ágeis, Gestão de Configuração e Testes de Software
Classificado em Computação
Escrito em em
português com um tamanho de 35,33 KB
Métodos Ágeis
Insatisfação com as metodologias “pesadas” de desenvolvimento de software entre os anos 1960 e 1990 deram origem aos métodos ágeis. Estes métodos focam-se no código em vez do desenho e são baseados em abordagens iterativas. Têm como objetivo a entrega rápida de software e a evolução rápida para responder às alterações dos requisitos.
- Objetivo das metodologias ágeis: Reduzir o peso do processo de desenvolvimento de software, diminuindo a documentação existente (respondendo rapidamente às alterações dos requisitos, evitando refazer muito trabalho).
Aplicação de Métodos Ágeis
Os métodos ágeis são adequados para:
- Desenvolvimento de software de pequena a média escala.
- Desenvolvimento de sistemas à medida, onde há participação ativa do cliente e poucas regras externas que afetem o software.
- Foco em equipas pequenas e muito bem integradas (é difícil escalar métodos ágeis para sistemas grandes).
Problemas dos Métodos Ágeis
- Pode ser difícil manter o interesse dos clientes no processo de desenvolvimento.
- Os membros da equipa de desenvolvimento podem não estar “preparados” para o envolvimento intenso característico.
- Estabelecer prioridades pode ser complicado quando existem vários interessados no sistema.
- Manter a simplicidade requer trabalho extra.
- Contratos podem ser um problema para os processos iterativos.
Métodos Ágeis e Manutenção de Software
A maior parte das empresas passa mais tempo a fazer manutenção de software do que a desenvolver software novo. É importante que os métodos ágeis garantam a manutenção de software, bem como o seu desenvolvimento inicial.
- Sistemas desenvolvidos através de métodos ágeis são manteníveis? (Considerando o foco no desenvolvimento e a minimização da documentação).
- Métodos ágeis podem ser usados de forma eficiente para evoluir um sistema para responder a pedidos de alterações? Podem surgir problemas se a equipa de desenvolvimento original não for mantida.
Comparação: Métodos Ágeis vs. Baseados em Planos
- Desenvolvimento baseado em planos: Etapas de desenvolvimento separadas; resultados produzidos no fim de cada etapa; etapas previamente planeadas; iterações dentro de cada etapa.
- Desenvolvimento ágil: Especificação, desenho, implementação e testes estão intercalados; outputs do processo de desenvolvimento são “negociados” durante todo o processo.
Extreme Programming (XP) – Boas Práticas
Em XP, o cliente ou utilizador faz parte da equipa de desenvolvimento e é responsável por tomar decisões sobre os requisitos.
- Requisitos de utilizador expressos através de cenários ou “user stories” (escritos em “cartões”).
- A equipa de desenvolvimento divide-os em tarefas, que servem de base para estimar o calendário e o orçamento.
- O Cliente escolhe as “histórias” para incluir na próxima release/versão, considerando as suas prioridades e a estimativa do tempo necessário para a implementação.
XP e Alterações
O bom senso no desenvolvimento de software sugere desenhar software para ser alterado, o que pode reduzir custos a longo prazo. O XP defende que este esforço antecipado não vale a pena, pois as alterações não podem ser antecipadas de forma fiável. Em vez disso, propõe a melhoria constante de código (refactoring) para tornar as alterações mais fáceis de implementar.
Refactoring
A equipa de desenvolvimento procura por possíveis melhorias no software e implementa essas melhorias, mesmo que não exista uma necessidade imediata. Melhora a compreensão do software e reduz a necessidade de documentação. Alterações são mais fáceis de implementar porque o código está bem estruturado e fácil de ler. No entanto, algumas alterações requerem fazer o refactoring da arquitetura, um processo mais dispendioso.
Exemplos de Refactoring:
- Reorganização da hierarquia de classes por forma a remover código repetido.
- “Arrumar” métodos e atributos por forma a tornar mais fácil perceber o que são.
- Substituir código por chamadas a métodos que foram/estão incluídos em bibliotecas.
Testes e XP
Testes são cruciais em XP – o software é testado antes de ser implementado.
Teste em XP:
- Testes antes do desenvolvimento.
- Testes incrementais a partir de cenários.
- Participação dos utilizadores no desenvolvimento e validação dos testes.
- Execução automática de testes para garantir que todos os testes são corridos para todas as releases/versões.
Programação por Pares - Vantagens
- O código não tem apenas um “dono” – não existe apenas um responsável pelo código.
- É um processo informal de revisão de código – cada linha de código é vista por, pelo menos, duas pessoas.
- Ajuda a fazer o refactoring do código.
Scrum
Scrum é uma abordagem genérica aos métodos ágeis, com foco na gestão do desenvolvimento iterativo em vez de aspetos específicos.
Composto por 3 etapas:
- Fase Inicial: Planeamento geral do projeto, estabelecimento de objetivos e desenho da arquitetura do software.
- Sprint Cycles: Em cada ciclo é desenvolvido parte do sistema.
- Fim do Projeto: Terminar documentação necessária, avaliar todos os aspetos do projeto (que lições podem ser tiradas e aplicadas nos próximos).
Sprint Cycle
Depois de escolhidas as funcionalidades, inicia-se o processo de desenvolvimento. Durante esta etapa, a equipa de desenvolvimento é “isolada” do cliente; as comunicações passam pelo Scrum Master.
- O papel do Scrum Master é proteger a equipa de desenvolvimento de distrações externas.
- No fim de cada sprint, o trabalho realizado é revisto e apresentado aos interessados, dando-se início ao novo sprint.
Scrum – Trabalho em Equipa
- Scrum Master: Facilitador que marca reuniões, gere o backlog, regista decisões, mede o progresso do projeto (considerando o backlog) e comunica com elementos externos.
- Toda a equipa participa em pequenas reuniões diárias: todos os membros partilham informação, descrevem o seu progresso desde a última reunião, quais os problemas que surgiram. Desta forma, toda a equipa sabe o que se passa no projeto.
- Se surgirem problemas, consegue-se planear o trabalho futuro para lidar com eles.
Scrum – Benefícios
- O produto é partido em partes mais pequenas e de fácil compreensão.
- Requisitos não estáveis não afetam o progresso do projeto.
- Toda a equipa tem visibilidade sobre todo o projeto – a comunicação na equipa é melhorada.
- Clientes têm entregas a tempo – feedback mais rápido sobre o produto.
- Confiança entre clientes e equipa de desenvolvimento.
Para se documentar os benefícios ou o valor, estes devem ser superiores ao custo de criar o documento ou ao custo de o manter. Se o custo de manter o documento não for superior ao seu benefício, ele é descartado (mantém-se só o útil). Por exemplo, se o cliente necessitar da parte teórica do produto, a documentação terá valor, logo será maior que o custo de criar, portanto cria-se.
Vê-se os comentários feitos no resultado e daí documenta-se o que foi feito e as especificações para depois entregar ao cliente. Priorizam comunicação verbal e direta.
Gestão de Configuração
O software muda frequentemente. Sistemas podem ser vistos como uma série de versões, e cada versão tem de ser mantida e gerida.
- Cada versão implementa pedidos de alterações, correções de falhas e adaptações diversas.
- Gestão de configurações: “Políticas” e processos para gerir alterações/mudanças – Essencial, pois é muito fácil perder o rasto das alterações e de quais as alterações que devem fazer parte de uma versão do sistema.
Atividades da Gestão de Configurações
- Gestão de alterações: Manter o “rasto” dos pedidos de alterações feitos pelos clientes e programadores, quais os custos dos impactos destas alterações e decidir quais as alterações que devem ser implementadas.
- Gestão de versões: Manter o rasto das várias versões dos componentes do sistema, garantindo que as alterações dos diferentes programadores não interferem com outras.
- Criação do sistema (System Building): Processo de construir componentes do sistema, dados e bibliotecas, para depois compilar tudo num sistema executável.
- Gestão de releases: Preparar o software para ser entregue e manter registo de todas as versões entregues (ao cliente).
Terminologia:
- Mainline: Uma sequência de baselines representando diferentes versões de um sistema.
- Release: Uma versão de um sistema que foi entregue aos clientes (ou outros utilizadores numa organização) para uso.
- Workspace: Uma área de trabalho privada onde o software pode ser modificado sem afetar outros programadores que possam estar a usar ou modificar esse software.
- Branching: A criação de uma nova codeline a partir de uma versão numa codeline existente. A nova codeline e a codeline existente podem então desenvolver-se independentemente.
- Merging: A criação de uma nova versão de um componente de software ao juntar versões separadas em codelines diferentes. Estas codelines podem ter sido criadas por um branch anterior de uma das codelines envolvidas.
- System Building: A criação de uma versão executável do sistema ao compilar e ligar as versões apropriadas dos componentes e bibliotecas que constituem o sistema.
Gestão de Alterações
Necessidades organizacionais e requisitos mudam durante a vida de um sistema; problemas têm de ser corrigidos e os sistemas têm de se adaptar a alterações no ambiente de funcionamento. A gestão de alterações tem como objetivo garantir que a evolução de um sistema é gerida por um processo e é dada prioridade às alterações mais urgentes.
Processo de gestão de alterações:
- Analisar custos e benefícios das alterações propostas.
- Aprovar alterações que valham a pena.
- Gerir o rasto dos componentes do sistema que foram/são alterados.
Análise de Alterações – Fatores de Análise
- Consequências de não fazer a alteração.
- Benefícios da alteração.
- Número de utilizadores afetados pela alteração.
- Custo de fazer a alteração.
- Ciclo de releases do produto.
Gestão de Versões
Gestão de versões é o processo de manter o “rasto” das diferentes versões dos componentes de software, dos itens de configuração e do sistema. Deve garantir que alterações feitas por diferentes programadores não interferem com o trabalho de outros programadores. Pode ser vista como um processo usado para gerir codelines e baselines.
Codelines e Baselines
- Uma codeline é uma sequência de versões de código, onde as versões mais recentes são derivadas de versões mais antigas. Aplica-se normalmente aos componentes do sistema, dando origem a diferentes versões de cada componente.
- Uma baseline é uma definição de um sistema específico. Especifica as versões dos componentes que são incluídos no sistema, juntamente com a especificação das bibliotecas usadas, ficheiros de configurações, etc.
Baselines
- Especificação de baselines: Recorrendo a linguagens/ferramentas específicas para definir quais os componentes que fazem parte de uma versão do sistema.
- Importância das Baselines: É necessário reconstruir uma versão específica do sistema e saber quais as versões exatas dos componentes que a compõem.
Sistemas de Gestão de Versões
- Identificação de versões e releases: Cada versão tem um identificador único.
- Gestão de armazenamento: Usam métodos que agilizam o armazenamento para reduzir o espaço necessário, visto que as versões dos componentes podem variar muito pouco entre si.
- Registo de todas as alterações: Todas as alterações feitas ao código devem ser registadas.
Sistemas de Gestão de Versões – Codeline Branches
Em vez de uma sequência linear de versões, podem existir várias sequências independentes (situação comum quando diferentes programadores trabalham de forma independente). Numa determinada etapa, será necessário juntar (merge) as codeline branches para criar uma nova versão do componente que inclua todas as alterações feitas. Se as alterações envolverem diferentes partes do código, o merge pode ser feito de forma automática, combinando as diferenças (deltas) das diferentes versões.
Construção do Sistema - System Building
Processo de criar uma versão completa e executável do sistema, compilando e ligando os vários componentes, bibliotecas externas, ficheiros de configuração, etc.
- Ferramentas de construção do sistema e ferramentas de gestão de versões devem comunicar entre si (fazer check-out de versões de componentes para fazer a construção).
- A descrição da baseline deve ser feita na ferramenta de construção do sistema.
Plataformas de Construção
- Plataformas de desenvolvimento: Incluem ferramentas de desenvolvimento (compiladores, editores, etc.). Programadores fazem checkout do código para um workspace privado antes de fazerem alterações.
- Build server: Usado para criar versões executáveis (releases) do sistema. Programadores fazem check-in do código no sistema de gestão de versões antes do build ser feito.
- Ambiente de produção: Plataforma onde o sistema é colocado em uso.
Build System
Funcionalidades:
- Script para gerar o build.
- Integração com o sistema de gestão de versões.
- Recompilação mínima.
- Criação de um sistema executável.
- Automação dos testes.
- Geração de documentação e relatórios.
System Build – Métodos Ágeis
- Check-out da mainline a partir do sistema de gestão de versões para o ambiente privado de desenvolvimento do programador.
- Construir o sistema e correr todos os testes de forma automática. Se falharem, a build tem problemas e deve informar-se o responsável pela última baseline.
- Fazer as alterações aos componentes do sistema.
- Construir o sistema no ambiente de desenvolvimento e correr todos os testes. Se os testes falharem, continuar o trabalho até correrem com sucesso.
- Depois de todos os testes passarem com sucesso, fazer check in no sistema de gestão de versões.
- Construir o sistema no build server e correr todos os testes (necessário se houve outros programadores que fizeram alterações desde o check out inicial). Se os testes falharem, é necessário corrigi-los no ambiente de desenvolvimento pessoal do programador.
- Se os testes passarem no build server, fazer commit das alterações como uma nova baseline na mainline do sistema.
Comandos e Fluxos de Trabalho (Git)
Criar um Novo Repositório
Cria-se uma nova pasta, abre-se e executa-se o comando GIT INIT para criar um novo repositório.
Obter um Repositório
Cria-se uma cópia de trabalho num repositório local executando o comando GIT CLONE /CAMINHO/PARA/O/REPOSITÓRIO. Quando usar um servidor remoto, o comando será GIT CLONE USUARIO@SERVIDOR:/CAMINHO/PARA/O/REPOSITORIO.
Fluxo de Trabalho
Os repositórios locais consistem em 3 “árvores” mantidas pelo Git:
- WORKING DIRECTORY: Contém os arquivos vigentes.
- INDEX: Funciona como uma área temporária.
- HEAD: Aponta para o último commit (confirmação) que fizemos.
Adicionar e Confirmar
Podemos propor mudanças (adicioná-las ao Index) usando GIT ADD ou GIT ADD *. Este é o primeiro passo no fluxo de trabalho básico do Git. Para realmente confirmar estas mudanças (isto é, fazer um commit), usa-se GIT COMMIT -m "COMENTÁRIOS DAS ALTERAÇÕES". Agora o arquivo é enviado para o HEAD, mas ainda não para o repositório remoto.
Enviando Alterações
As alterações agora estão no HEAD da sua cópia de trabalho local. Para enviar estas alterações ao seu repositório remoto, execute GIT PUSH ORIGIN MASTER. Altere o MASTER para qualquer ramo (branch) desejado, enviando as suas alterações para ele. Se não clonou um repositório existente e quer conectar o seu repositório a um servidor remoto, você deve adicioná-lo com GIT REMOTE ADD ORIGIN. Agora você é capaz de enviar as suas alterações para o servidor remoto selecionado.
Ramificando (Branching)
Branches (“ramos”) são utilizados para desenvolver funcionalidades isoladas umas das outras. O branch MASTER é o branch “padrão” quando você cria um repositório. Use outros branches para desenvolver e mescle-os (MERGE) ao branch MASTER após a conclusão.
- Cria-se um novo branch chamado “
FUNCIONALIDADE_X” e seleciona-o usandoGIT CHECKOUT -b FUNCIONALIDADE_X. - Retorna-se para o
MASTERusandoGIT CHECKOUT MASTER. - Remove-se o branch da seguinte forma:
GIT BRANCH -d FUNCIONALIDADE_X. - Um branch não está disponível a outros a menos que você o envie para o seu repositório remoto:
GIT PUSH ORIGIN.
Atualizar e Mesclar (Pull e Merge)
Para atualizar o seu repositório local com a versão mais nova, execute GIT PULL na sua pasta de trabalho para obter e fazer o merge (mesclar) alterações remotas. Para fazer merge de outro branch ao seu branch ativo (ex: MASTER), use GIT MERGE.
Em ambos os casos, o Git tenta fazer o merge das alterações automaticamente. Infelizmente, isto nem sempre é possível e resulta em conflitos. Você é o responsável por fazer o merge destes conflitos manualmente, editando os arquivos exibidos pelo Git. Depois de alterar, você precisa marcá-los como merged com GIT ADD. Antes de fazer o merge das alterações, você pode pré-visualizá-las usando GIT DIFF >BRANCH DESTINO>.
Rotulando (Tagging)
É recomendado criar rótulos para releases de software. Você pode criar um novo rótulo chamado 1.0.0 executando o comando GIT TAG 1.0.0 1b2e1d63ff. O 1b2e1d63ff representa os 10 primeiros caracteres do ID de commit que você quer referenciar com o seu rótulo. Você pode obter o ID de commit com GIT LOG. Você pode usar menos caracteres do ID de commit, ele somente precisa ser único.
Sobrescrever Alterações Locais
No caso de se ter feito algo errado, você pode sobrescrever as alterações locais usando o comando GIT CHECKOUT --. Isto substitui as alterações na sua árvore de trabalho com o conteúdo mais recente no HEAD. Alterações já adicionadas ao index, bem como novos arquivos, serão mantidos.
Se, em vez disso, desejar remover todas as alterações e commits locais, recupere o histórico mais recente do servidor e aponte para o seu branch MASTER local desta forma: GIT FETCH ORIGIN e GIT RESET --HARD ORIGIN/MASTER.
Configurações e Atalhos Úteis
- Interface gráfica padrão:
GITK - Usar saídas do Git coloridas:
GIT CONFIG COLOR.UI TRUE - Exibir log em apenas uma linha por commit:
GIT CONFIG FORMAT.PRETTY ONELINE - Fazer inclusões interativas:
GIT ADD -i - Mudar de branch:
GIT COMMIT -a -m 'made a change'//GIT CHECKOUT MASTER//git commit -a -m 'made other changes' - Mudar para novo branch:
git checkout -b iss53 - Atalho para criar e mudar de branch:
git branch iss53//git checkout iss53 - Fazer alterações e um commit:
vim index.html$git commit -a -m 'added a new footer [issue 53]'
Fluxo de Branches
Cenário de Branching e Merging:
- Atribuída uma tarefa/issue – Cria-se um branch para trabalhar nessa tarefa/issue.
- Trabalha-se nessa tarefa/issue.
- Recebe-se “ordens” para trabalhar num problema crítico!
- Muda-se para o branch de “produção”.
- Cria-se um branch para o problema crítico.
- Trabalha-se no problema.
- Faz-se merge do branch criado para resolver o problema.
- Muda-se para o branch da tarefa/issue original.
Fluxo de Branches de Longa Duração
- master: branch principal – branch de produção.
- develop: branch de desenvolvimento – código “estável” – usado para testar estabilidade e integração – usado para fazer merge dos branches dos issues/tarefas.
- topic: para desenvolver issues/tarefas.
Testar Programas
Objetivo dos Testes
Testes servem para mostrar que um programa faz o que deve fazer, e para descobrir problemas e erros antes de ser colocado em utilização.
Como se testa o software:
- Executa-se o programa com dados artificiais.
- Verifica-se os resultados, procura-se erros, anomalias ou informação sobre os atributos não funcionais do sistema.
- Testes podem revelar a presença de erros, mas NÃO a sua ausência.
- Testes fazem parte do processo de verificação e validação mais genérico, que também inclui técnicas de validação estáticas.
- Demonstrar ao programador e ao cliente que o software está de acordo com os requisitos: Para software específico (feito à medida), pelo menos um teste para requisito no documento de requisitos. Para software genérico, testes para todas as funcionalidades do sistema.
- Encontrar situações em que o comportamento anómalo do software não está de acordo com os requisitos: Testes de defeitos (defect testing) têm como objetivo remover comportamentos inesperados do sistema (“crashes”, interações indesejadas, corrupção de dados, etc.).
Testes de Validação e de Defeitos
- 1º Objetivo (Testes de Validação): Onde se espera que o sistema tenha o comportamento esperado, usando um conjunto de testes que refletem a utilização normal do sistema.
- 2º Objetivo (Testes de Defeitos): Testes desenhados para revelar defeitos e erros. Testes deliberadamente “obscuros” que não precisam de refletir o comportamento normal do sistema.
Objetivo do Processo de Testes
- Testes de validação: Demonstrar ao programador e ao cliente que o software está de acordo com os requisitos. Um bom teste mostra que o sistema funciona como deve funcionar.
- Testes de defeito: Encontrar erros e problemas que dão origem a comportamentos anormais e que não estão de acordo com as especificações. Um bom teste faz com que o sistema funcione de forma incorreta, expondo os seus erros e defeitos.
Verificação VS Validação
- Verificação: “Estamos a criar o produto de forma correta?” – O software deve estar de acordo com as especificações.
- Validação: “Estamos a construir o produto certo?” – O software deve fazer aquilo que o utilizador realmente necessita.
Inspeções de Software
Análise e verificação da representação estática do sistema por forma a encontrar problemas (Verificação estática).
Testes de Software
Observação do comportamento do sistema (Verificação dinâmica). O sistema é executado com dados de testes, verificando-se o seu comportamento.
Inspeção de Software - Vantagens
- Durante os testes, os erros podem esconder outros erros. Como as inspeções são um processo estático, não existe a preocupação com interações entre erros.
- Versões incompletas do sistema podem ser inspecionadas sem custos adicionais (se o programa estiver incompleto, é necessário desenvolver testes específicos para testar os componentes disponíveis).
- Para além de se procurarem defeitos, inspeções podem ser úteis para encontrar atributos de qualidade de um programa: satisfação de standards, portabilidade e manutenção.
Inspeções e Testes
- Inspeções e testes são atividades complementares e ambas devem ser usadas no processo de validação e verificação.
- Inspeções podem verificar se o sistema está de acordo com as especificações, mas não podem ser usadas para verificar se o sistema está de acordo com as reais necessidades do cliente.
- Inspeções não podem ser usadas para verificar características não funcionais (desempenho, usabilidade, etc.).
Testes - Etapas
- Testes durante a fase de desenvolvimento (Development testing): Sistema é testado durante a etapa de desenvolvimento para procurar erros e defeitos.
- Testes de Release: Testes a uma versão completa do sistema (release), feitos por equipa de testes diferente, antes de ser entregue aos utilizadores.
- Testes de utilizador: Testes feitos ao sistema por utilizadores reais, no seu ambiente de trabalho.
Testes durante a Fase de Desenvolvimento
Testes de desenvolvimento incluem todas as atividades de testes realizadas durante o desenvolvimento do sistema:
- Testes unitários: “Unidades” ou objetos individuais do sistema são testados (testar funcionalidades de objetos ou métodos).
- Testes de componentes: Várias unidades são integradas para criar componentes (testar a interface entre os vários componentes).
- Testes ao sistema: Todos os componentes são integrados (teste do sistema como um todo, testar interações entre componentes).
Testes Unitários
Processo de testar componentes individuais de forma isolada. É um processo de testes de defeitos.
“Unidades” podem ser:
- Funções ou métodos individuais de um objeto.
- Objetos com vários atributos e métodos.
- Componentes com interfaces bem definidos para aceder às suas funcionalidades.
Testar Objetos
Cobertura completa dos testes a uma classe envolve:
- Testar todas as operações associadas com um objeto.
- Especificar e ler todos os atributos de um objeto (Setting e getting).
- Testar o objeto em todos os possíveis estados.
Herança pode tornar difícil o desenho de testes a objetos, pois a informação não está localizada.
Testes Autónomos
Sempre que possível, os testes unitários devem ser automatizados (executar e verificar os testes sem intervenção manual).
Frameworks de testes (ex: JUnit):
- Fornecem classes de testes genéricas, estendidas para criar testes específicos.
- Permitem escrever e executar os testes.
- Permitem executar todos os testes implementados e gerar relatórios.
Componentes dos Testes Autónomos
- Setup: Inicialização do sistema e do teste.
- Inputs: Dados de entrada.
- Outputs esperados: Resultado que se espera obter.
- Chamada/Execução: Método, função ou objeto é testado.
- Verificação/Assertion: Comparação do output da chamada com o output esperado. Teste passou com sucesso: True; Teste não passou: False.
Testes Unitários - Eficácia
Testes unitários devem mostrar que os componentes testados fazem o esperado e que, se houver defeitos ou problemas, estes devem ser revelados pelos testes. Origem a dois tipos de testes:
- Testes que refletem o uso normal do sistema e mostram que o componente funciona de acordo com o esperado.
- Testes que usam inputs pouco comuns ou anormais para verificar que o sistema é capaz de os processar e não fazem o sistema “crashar”.
Estratégias de Testes
- Particionar os testes: Identificar grupos de inputs que têm características comuns e devem ser processados da mesma forma. Escolher testes das categorias identificadas.
- Baseado em regras/guias: Testes escolhidos de acordo com algumas regras baseadas na experiência dos programadores ou nos erros comuns de programação.
Particionar os Testes
Dados de input e de output enquadram-se em diferentes classes – todos os membros de uma classe estão relacionados.
- Cada classe tem uma Partição Equivalente: O sistema comporta-se de forma igual para cada membro da classe. Casos de teste devem ser escolhidos a partir de cada partição.
Guias de Testes - Sequências
Testar usando sequências (listas, arrays, etc.) apenas com um valor, testar o primeiro, último e elemento do meio, e testar sequências com tamanho zero.
Guias de Testes - Genéricas
- Escolher inputs que forcem o sistema a gerar todas as mensagens de erros possíveis.
- Escolher inputs que possam causar “buffer overflows”.
- Repetir os mesmos inputs várias vezes seguidas.
- Forçar a geração de outputs inválidos.
- Forçar processos que produzam resultados muito pequenos ou muito grandes.
JUnit: Framework de Testes
JUnit é um framework de testes com integração com IDEs, execução automática de testes e baseado em anotações. Os testes estão organizados em classes de teste e suítes de teste.
- Classes de teste: Uma classe de testes para cada classe. Pelo menos um método de testes para cada método-caso em que tudo corre bem.
- Convenções: Usar os mesmos nomes de pacotes; nomes baseados nas classes que estão a ser testadas (Classe a ser testada:
MyClass. Classe de testes:MyClassTest). - Métodos de teste: Métodos que implementam um caso de testes. Podem implementar o maior número possível de casos de testes ou criar-se métodos de teste para cada caso.
- Suite de teste: Conjunto de classes de teste.
- Métodos de teste: Executados num ambiente isolado (não devem depender de outros testes); baseados em anotações.
Estrutura do Teste
- Setup do teste: Inicialização de variáveis, definição do resultado esperado.
- Execução: Método que se quer testar.
- Assert: Comparação entre o resultado obtido e o resultado esperado.
Convenções: Nomes descritivos; O que deve ou não acontecer.
Anotações (Annotations)
Usadas para descobrir, organizar e ativar testes.
@BeforeClass: Executado antes de todos os testes de uma classe de testes; usado para inicializações.@AfterClass: Executado depois de todos os testes de uma classe de testes; atividades de limpeza.@Before: Executado antes de cada teste de uma classe de testes; setup do ambiente de execução do teste.@After: Executado depois de cada teste da classe de testes; limpeza de todos os testes.@Test: Usado para marcar/anotar um método de testes.@Test(timeout = ): Definir o tempo máximo de execução; se o tempo exceder o valor, o teste falha.@Test(expected=.class): Teste passa se for lançada uma exceção.@Ignore: Usado para ignorar testes (testes incompletos/não terminados); opcional – adicionar uma string.
Assertions (Afirmações)
Método usado para determinar se um teste passou ou não; comparação entre o valor esperado e o valor calculado.
assertEquals: Verifica se dois objetos são iguais.assertEquals (arrays): True se os arrays têm o mesmo tamanho e todos os elementos nas mesmas posições são iguais.assertNull: Verifica se um objeto é null.assertNotNull: Verifica se um objeto não é null.assertSame: Verifica se dois objetos são o mesmo.assertNotSame: Verifica se dois objetos não são o mesmo.assertTrue: Verifica se uma condição booleana é verdadeira.assertFalse: Verifica se uma condição booleana é falsa.fail: Obriga um teste a falhar.
Dicas para os Testes Unitários
- Usar variáveis de classes nas classes de testes.
- Ser exaustivo (testar o máximo possível).
- Não esquecer as exceções (garantir que as exceções são lançadas quando devem ser lançadas).
- Documentar os asserts (útil quando os testes falham).
- Verificar a cobertura dos testes (periodicamente, usar os resultados).