Verificação
Roberto A. Bittencourt (Professor, UEFS) Adaptado do material de Václav Rajlich
Verificação
Todas as mudanças no código
precisam ser verificadas
refatorações atualizações
Dificuldades essenciais
Programadores produzem trabalho
imperfeito muito frequentemente
Defeitos são chamados de “bugs”
Verificação encontra bugs
Initiation Concept Location Impact Analysis Prefactoring Actualization Postfactoring Conclusion V E R I F I C A T I O N
Técnicas de verificação
Muitas técnicas de verificação têm sido
pesquisadas e propostas
Prática atual
testes
Testes
Testes executam o programa ou suas partes
Entrada especificada para a execução
Compara as saídas da execução com as saídas
previamente especificadas
Relata se houve algum desvio
Testes são normalmente organizados em uma
suíte de testes
Incompletude de testes de software
“Testes podem demonstrar a a presença de
bugs, mas não a sua ausência. “
Não importa quantos testes tenham sido
feitos, bugs residuais podem continuar
escondidos no código
Não foram alcançados e revelados por nenhum
teste.
Nenhuma suíte de teste pode garantir que o
Problema da Parada de Turing
Razões teóricas para a incompletude de
testes
É teoricamente impossível criar uma suíte de
testes perfeita
Programadores têm tentado fazer o melhor
possível dentro destas circunstâncias
Técnicas de teste não podem garantir a correção
total de um software
Testes bem projetados chegam próximo do
Testes do código novo vs. antigo
Testes do código novo
Novos testes devem ser escritos com o novo
código
Testes do código antigo
Os testes devem garantir que o código antigo não
é quebrado pela mudança
Testes de regressão
Evitar regressão do que já funcionava no software
Merriam Webster: “regressão” = “tendência ou
Variedade de testes de software
Configuração
Workspace do programador
Gerência de configuração da equipe
Estratégia
estrutural
de unidade
funcional
Funcionalidade
antiga (teste de regressão)
nova
Testes de aceitação
Testes funcionais finais
Tanto do código novo como do antigo
Normalmente feitos durante a fase de
conclusão da mudança
Testam a funcionalidade completa do software Os stakeholders podem avaliar o progresso do
Composição da suíte de testes
Testes de unidade
Testam uma classe específica
Testes funcionais
Testam uma funcionalidade específica do
programa inteiro
O manual do usuário ou a interface gráfica do usuário
podem guiar a criação dos testes funcionais
Todas as funcionalidades que estão disponíveis ao
usuário devem ser testadas
Harness (scaffolding)
Drivers de teste
Implementam o suporte para os testes
Stubs
Implementam a substituição de classes ou
subsistemas que estejam faltando
Simulação do ambiente
Harness = drivers + stubs + simuladores
Código de produção vs. harness
Desenvolvedores vs. testadores
Código de harness e de produção
Código de produção vai para o usuário
Harness permanece dentro do grupo de
desenvolvimento
Evolução paralela de ambos
code 1 harness 1 code 2 harness 2 code 3 harness 3
Cobertura
Não podemos garantir a correção completa de
um código através de testes
Mas podemos garantir que cada unidade do
código testado é executada pelo menos uma
vez
Isto garante que ao menos alguns dos bugs são
descobertos
Em particular, os bugs que são levantados por esta
Granularidade de métodos
calcSubTotal(), calcTotal(), getPrice(),
setPrice(double)
Cada um deles é executado pelo menos uma vez
Parece uma abordagem muito crua
Mas é sistemática
Garante melhor correção que a seleção aleatória
Cobertura de instruções
Garante que cada instrução do programa
executa ao menos uma vez
Suíte de testes mínima que não tem testes
redundantes
Testes que cobrem apenas instruções já são
cobertos por outros testes
Suíte de testes mínima realiza eficientemente
a cobertura
Exemplo
read (x); read (y);
if x > 0 then write ("1");
else
write ("2");
end if;
if y > 0 then write ("3");
else write ("4");
end if;
{<x = 2, y = 3>}
Cobertura incompleta Não cobre write(“2){<x =2, y
3>,<x=9,y=1>,
<x=-1,y=-1>}
Cobertura completa Não mínima <x =9, y=1> é redundante{<x =2, y 3>,
<x=-1,y=-1>}
Suíte de testes completa mínima
É fácil escrever o primeiro teste
Não importa o que ele cobre, ele acrescenta à
cobertura da suíte de testes
Número crescente de testes
As instruções não cobertas são em menor
quantidade
Torna-se crescentemente difícil criar novos testes
nestas instruções remanescentes ainda não cobertas
Testes de unidade
Testes dos módulos individuais
Testes de classes e métodos
Testes da funcionalidade composta
A classe está sendo testada junto com todas as
classes que darão suporte a ela
Exemplo de driver de teste
public class TestItem { Item testItem;
public void testCalcSubTotal() {
assert(testItem.calcSubTotal(2, 3) == 6);
assert(testItem.calcSubTotal(10, 20) ==30); }
public void testCalcTotal() {
assert(testItem.calcTotal(0, 5) == 5);
assert(testItem.calcTotal(15, 25) == 40); } } ;
Testando responsabilidade local
Driver + stub
stub simula fornecedores
Parte do harness
Razões - As classes fornecedoras:
não estão disponíveis
não foram testadas ainda
a confiança nelas ainda é baixa
dão suporte a um contrato limitado (pré-condição
limitada)
Técnicas de stubbing
Algoritmo menos eficaz
Mais fácil de implementar
O teste torna-se menos eficiente
Desenvolvedores fazendo testes , impacto aceitável
Pré-condições limitadas do stub
Simplificam o código do stub substancialmente
Converter a data em um dia da semana
O stub o faz apenas para um mês selecionado
Técnicas de stubbing (cont.)
Intervenção do usuário
Interrompe-se o teste, e o usuário provê a
resposta correta
Prático apenas se o stub é executado apenas algumas
poucas vezes durante o teste
Usuário humano pode entrar valores incorretos
Contrato de substituição
Pós-condição rápida mas incorreta Técnica mais controversa de stubbing Ainda provê resultados úteis
Testes funcionais
Testam a funcionalidade do programa
completo
Programa com GUI: testar cada função
“gravação” para testes futuros
Cobertura
Testes de regressão
Depois de uma mudança, programadores
retestam o código
Reestabelecer a confiança que as características
anteriores do software mantém-se funcionando
Mudança pode ter introduzido por engano bugs
perdidos em partes anteriormente intactas
Testes do passado constituem o núcleo de
uma suíte de testes de regressão
suíte de testes cresce frequentemente
Testes obsoletos
Casos de teste quebrados que não podem
executar
Eles já não interfaceiam mais com o software
Testes que não atendem a seus propósitos
Um caso de teste testando os limites de uma
faixa torna-se obsoleto quando a faixa é
modificada
Testes que deixam de prover um resultado
Inspeção de código
Alguém diferente do autor lê o código
Checa sua corretude Relata bugs
Inspeção de código não requer a execução do
sistema
Pode ser aplicado a código incompleto ou a outros
artefatos
Eficácia da inspeção de código
“Força do ‘hábito”
As pessoas tornam-se cegas a seus próprios erros
Depois de ler o código várias vezes
Programadores deixam de ler o código
Lembram de memória o que o código devia conter Alguns erros escapam a sua atenção repetidamente
Um leitor diferente aponta estes erros fácil e
Inspeções e testes são complementares
Alguns bugs são facilmente descobertos por
testes
Eles aparecem em todos os testes
Às vezes eles são difíceis de serem vistos por humanos exemplo: ortografia incorreta de identificadores longos
Alguns bugs são difíceis de se achar através
de testes
É difícil criar um teste que os encontre
Leitores humanos podem achá-los facilmente Exemplo: uma divisão por zero
Inspeção de diferentes artefatos
Inspeções também podem checar se
diferentes artefatos concordam entre si
O código corresponde à solicitação de mudança? O modelo UML corresponde ao verdadeiro código?
Inspeções não podem checar atributos não
funcionais
Walkthroughs
Processo de inspeção estruturado
Equipe de Walkthrough
Ao menos quatro membros
O autor do código inspecionado
Um moderador
Processo de walkthrough
Preparação
O código ou outros artefatos são distribuídos ao
time de inspeção
Um participante é selecionado para inspecionar o
documento detalhadamente
Reunião de Walkthrough
Todos os membros da equipe participam
O grupo completo caminha através do documento
Reunião de walkthrough
O leitor anota os erros, omissões e
inconsistências do código
Os outros membros da equipe adicionam suas
próprias observações
O moderador dirige a reunião
Anota os erros descobertos Produz um relatório
Recomendações, que documentos devem ser