• Nenhum resultado encontrado

Aula 05Teste Automatizado –Partes I e II

N/A
N/A
Protected

Academic year: 2022

Share "Aula 05Teste Automatizado –Partes I e II"

Copied!
43
0
0

Texto

(1)

Aula 05

Teste Automatizado – Partes I e II

Alessandro Garcia LES/DI/PUC-Rio

Março de 2019

(2)

2 / 30 LES/DI/PU

Exercício sobre Introdução à Teste

• Se organizem em grupos (2 a 3 pessoas): de preferência o grupo que irá realizar os trabalhos na disciplina

• Escrevam em uma folha de papel o seguinte cabeçalho:

EXERCÍCIO 1: INTRODUÇÃO À TESTE Data: (data de hoje)

Integrantes: (nome dos integrantes do grupo)

Atividade 1: Casos de teste para uma função

(3)

Exemplo: criando casos de teste

• Um bom conjunto de casos de teste é aquele que possui alta probabilidade em revelar defeitos na função ou no módulo que a contém

int verificaTriangulo (int segmentoAB, int segmentoAC, int segmentoBC);

EXERCÍCIO 1: INTRODUÇÃO À TESTE Data: (data de hoje)

Integrantes: (nome dos integrantes do grupo)

Atividade 1: Casos de teste para uma função

•C1: (a, b, c) -> (d)

•C2: (x, y, z) -> (k)

•...

(4)

4 / 30 LES/DI/PU

Criando casos de teste com a especificação

EXERCÍCIO 1: INTRODUÇÃO À TESTE Data: (data de hoje)

Integrantes: (nome dos integrantes do grupo)

•Atividade 1: Casos de teste para uma função

•C1: (a, b, c) -> (d)

•C2: (x, y, z) -> (k)

•...

•Atividade 2: Casos de teste com especificação

•C1: (a, b, c) -> (d)

•C2: (x, y, z) -> (k)

(5)

Criando casos de teste com a especificação

A função recebe como entrada três valores inteiros. Cada valor

corresponde ao segmento de um lado do triângulo. Assim, o primeiro valor corresponde ao segmento do lado AB, o segundo valor ao segmento do lado AC e o terceiro valor correspondem ao segmento do lado BC. Esses três segmentos correspondem aos três lados que formam o triângulo ABC.

A função recebe os três valores como entrada e verifica que tipo de triângulo é formado com base nos três segmentos. Por fim, a função

retorna a quantidade de lados que possuem o mesmo tamanho. Assim, se o triângulo for equilátero, ou seja, os três lados possuem o mesmo

tamanho, então a função retorna 3. Se o triângulo for isósceles (dois lados com o mesmo tamanho), então a função retorna 2. Se o triângulo for

escaleno (os três lados possuem tamanhos diferentes), então a função

retorna 0. Caso os três valores não correspondam a um triângulo, a função retorna o código -1.

int verificaTriangulo (int segmentoAB, int segmentoAC, int segmentoBC);

(6)

6 / 30 LES/DI/PU

Casos de teste

• Um dos problemas comuns ao testar software é determinar quando os módulos foram suficientemente testados

• Mesmo usando a especificação, é difícil produzir bons casos de teste que sejam capazes de revelar a existência de defeitos

• Por exemplo, para o problema do triângulo, é necessário apenas quatro casos de testes para se testar a função em termos dos três tipos de triângulo

1 caso válido para triângulo equilátero (retorna 3) 1 casos válidos para triângulo isósceles (retorna 2) 1 caso válido para triângulo escaleno (retorna 0)

1 caso válido para valores que não correspondem a um triângulo (retorno -1)

(7)

Criando casos de teste com a especificação

int verificaTriangulo(int segAB, int segAC, int segBC){

if ((segAB == segAC) && (segAC == segBC)){

return 3; //equilatero }

if (((segAB== segAC) && (segAC != segBC)) ||

((segAC == segBC) && (segAB != segAC)) ||

((segAB == segBC) && (segBC != segAB))){

return 2; //isosceles }

if ( (segAB != segAC) && (segAC != segBC)) { return 0; //escaleno

}

return -1;

}

Qual é a saída para o caso de teste (2, 1, 1)?

(8)

8 / 30 LES/DI/PU

Criando casos de teste com a especificação

• Qual é a saída para o caso de teste (2, 1, 1)?

2 -> triângulo isósceles

• Esses valores não correspondem a um triângulo.

• Para se ter um triângulo, é necessário que ele tenha em cada um dos seus lados, uma medida menor que a soma das medidas dos outros dois lados.

AB < AC + BC AC < AB + BC BC < AB + AC

–Observação: Se AB for o maior lado, para existir um triângulo, basta que AB < AC + BC.

(9)

Função verificaTriangulo

int verificaTriangulo(int segAB, int segAC, int segBC){

if ( (segAB < segAC+ segBC) && (segAC< segAB + segBC) && (segBC< segAB + segAC)){

if ((segAB == segAC) && (segAC == segBC)){

return 3; //equilatero }

if (((segAB== segAC) && (segAC != segBC)) ||

((segAC == segBC) && (segAB != segAC)) ||

((segAB == segBC) && (segBC != segAC))){

return 2; //isosceles }

if ( (segAB != segAC) && (segAC != segBC)) { return 0; //escaleno

} }

return -1;

} E agora, o código está livre de defeitos???

(10)

10 / 30 LES/DI/PU

Completude dos casos de teste

• Um bom conjunto de casos de teste seria aquele que cobrisse os seguintes casos:

1 caso válido para triângulo equilátero (retorna 3) 1 caso válido para triângulo escaleno (retorna 0) 3 casos válidos para triângulo isósceles (retorna 2) 3 casos para testar Lk > Li + Lj (retorna -1)

3 casos para testar Lk = Li + Lj (retorna -1)

3 casos para testar um dos lados igual a 0 (retorna -1)

3 casos para testar um dos lados menor do que 0 (retorna -1)

• Testes somente são capazes de mostrar a presença de

faltas, mas não a ausê ncia delas.

(11)

Especificação

• Objetivo dessa aula

– Apresentar os uma técnica para a criação testes automatizados e mostrar como desenvolver dirigido por testes.

Referência básica:

Monografia: Arcabouço para a Automação de Testes de Programas Redigidos em C; contido no arquivo TesteAutomatizado.zip acessível para download através do site da disciplina, aba: Software

Referência adicional:

Beck, K.; Test-Driven Development by Example; Reading, Massachusetts:

Addison-Wesley; 2003

Slides adaptados de: Staa, A.v. Notas de Aula em Programacao Modular;

2008.

(12)

12 / 27

Sumário

• Automação simplória

• Arcabouço de apoio ao teste automatizado

• Linguagem de diretivas de teste

• Interpretador de diretivas

• Exemplo de uso do arcabouço

• Arquitetura do arcabouço

• Vantagens e desvantagens

(13)

Como testar um módulo?

• Uso de um módulo controlador do teste

desenvolvido que auxilia a execução de casos de teste para testar um módulo sob teste

• Três formas:

Teste manual: controlador exibe um menu e

comparação é feita pelo próprio testador à olho nu – Teste de comparação automatizado:

• Casos de teste são implementados em C

• Casos de teste são redigidos em scripts em uma linguagem com uma sintaxe própria

(14)

14 / 30

Exemplo simplório

. . .

ContaCaso ++ ;

if ( CriarArvore( ) != ARV_CondRetOK ) {

printf( "\nErro ao criar árvore" ) ; ContaFalhas ++ ;

}

ContaCaso ++ ;

if ( InserirEsquerda('a' ) != ARV_CondRetOK ) {

printf( "\nErro ao inserir nó raiz da árvore" ) ; ContaFalhas ++ ;

}

ContaCaso ++ ;

if ( IrPai( ) != ARV_CondRetEhRaiz ) {

printf( "\nErro ao ir para pai de nó raiz" ) ; ContaFalhas ++ ;

}

. . .

instância de caso de teste

compara obtido com o esperado

É NECESSÁRIO REPETIR O MESMO CÓDIGO PARA CADA CASO DE TESTE

(15)

Por que é simplório?

• O código é muito repetitivo

– viola a regra de evitar duplicações de código

(16)

16 / 30 LES/DI/PU

Exemplo simplório

. . .

ContaCaso ++ ;

if ( CriarArvore( ) != ARV_CondRetOK ) {

printf( "\nErro ao criar árvore" ) ; ContaFalhas ++ ;

}

ContaCaso ++ ;

if ( InserirEsquerda('a' ) != ARV_CondRetOK ) {

printf( "\nErro ao inserir nó raiz da árvore" ) ; ContaFalhas ++ ;

}

ContaCaso ++ ;

if ( IrPai( ) != ARV_CondRetEhRaiz ) {

printf( "\nErro ao ir para pai de nó raiz" ) ; ContaFalhas ++ ;

}

. . .

Muita repetição

(17)

Por que é simplório?

• O código é muito repetitivo

– viola a regra de evitar duplicações de código

• O módulo controlador do teste pode tornar-se muito extenso

– dificulta verificar se o teste é um bom teste

• O código não produz um laudo do teste

– as mensagens impressas não explicitam o porquê da falha

quais foram os valores que causaram a mensagem?

(18)

18 / 27

Como reduzir o volume de repetição?

• Criar um arcabouço (framework) com as funções de comparação.

Framework

Pontos de extensão

Aplicação

Serviço do arcabouço

(19)

Exemplo de uma função de comparação

TST_tpCondRet TST_CompararInt( long ValorEsperado , long ValorObtido , char * pMensagem ) {

if ( ValorObtido != ValorEsperado ) {

ContaFalhas ++ ;

fprintf( pArqLog , "\nErro >>> %s" , pMensagem ) ; fprintf( pArqLog , "Deveria ser: %ld É: %ld" ,

ValorEsperado , ValorObtido ) ; return TST_CondRetErro ;

} /* if */

return TST_CondRetOK ;

} /* Fim função: TSTG &Comparar inteiro */

mensagem de erro gera-se um laudo de

erros

(20)

20 / 27

Como fica o código agora?

. . .

if ( TST_CompararInt( ARV_CondRetOK , CriarArvore( ) ,

"Erro ao criar árvore" ) != TST_CondRetOK ) {

return ARV_CondRetOK;

}

if ( TST_CompararInt( ARV_CondRetOK , InserirEsquerda('a' ) ,

"Erro ao inserir nó raiz da árvore" ) != TST_CondRetOK ) {

return ARV_CondRetOK;

}

if ( TST_CompararInt( ARV_CondRetEhRaiz , IrPai( ) ,

"Erro ao ir para pai na raiz" ) != TST_CondRetOK ) {

return ARV_CondRetEhRaiz;

}

. . .

Opção 1: massa de teste escrita com a própria linguagem de programação

(21)

Como fica o código agora? Melhorou?

. . .

if ( TST_CompararInt( ARV_CondRetOK , CriarArvore( ) ,

"Erro ao criar árvore" ) != TST_CondRetOK ) {

return ; }

if ( TST_CompararInt( ARV_CondRetOK , InserirEsquerda('a' ) ,

"Erro ao inserir nó raiz da árvore" ) != TST_CondRetOK ) {

return ; }

if ( TST_CompararInt( ARV_CondRetEhRaiz , IrPai( ) ,

"Erro ao ir para pai na raiz" ) != TST_CondRetOK ) {

return ; }

if ( TST_CompararInt( ARV_CondRetOK , InserirEsquerda(‘b' ) ,

"Erro ao inserir nó raiz da árvore" ) != TST_CondRetOK ) {

return ; }

. . .

ainda é repetitivo, verboso

(22)

22 / 30

Melhorou?

• Sim, pode-se adicionar mais funcionalidades ao arcabouço

– outros comparadores

• Sim, ganhou-se uniformidade

• Não, o código continua extenso

• Não, pára na primeira falha encontrada

(23)

3ª. Opção: nossa solução

• A solução que adotamos é um módulo interpretador inspirado no JUnit

– permite-se criar linguagens de script para definição da massa de teste

– Objetivos:

redução da complexidade e repetição do código de teste

abstração de detalhes para o testador

escreve-se cada caso de teste em uma única linha desnecessário alterar o módulo controlador de teste

torna-se a linguagem muito próxima da “linguagem natural”

• Para quem programa Java, C++, C# e outros, existem

diversos arcabouços: JUnit, CppUnit, CSUnit, ...

(24)

24 / 27

Exemplo de uma massa de teste

. . .

== Criar árvore

=criar OK

=irdir ArvoreVazia

== Inserir à direita

=insdir CharA OK

== Obter o valor inserido

=obter CharA OK

== Ir para no pai, não tem

=irpai EhRaiz

== Inserir à esquerda

=insesq CharB OK

=obter CharB OK

== Ir para no pai, tem

=irpai OK

=obter CharA OK . . .

(25)

Exemplo de uma massa de teste

. . .

== Criar árvore

=criar OK

=irdir ArvoreVazia

== Inserir à direita

=insdir CharA OK

== Obter o valor inserido

=obter CharA OK

== Ir para no pai, não tem

=irpai EhRaiz

== Inserir à esquerda

=insesq CharB OK

=obter CharB OK

== Ir para no pai, tem

=irpai OK

=obter CharA OK . . .

Se fôssemos escrever estes mesmos casos de teste com a abordagem anterior, teríamos que produzir 9 x 5 = 45 linhas de código em C.

(26)

Definição da Linguagem de Script de Teste

Passo 1 – definir comandos (não são declarados no início do script):

Sintaxe: =<nome da função> <lista parâmetros> <condição retorno>

Exemplo – Módulo Árvore:

=criar < > <OK, FaltouMemoria, etc...>

=irdir < > <OK, ArvoreVazia, etc...>

=insdir <char> <OK, ValorInvalido, FaltouMemoria, etc..>

=irpai < > <OK, EhRaiz, etc...>

=obter <char> <OK, ValorInvalido, etc...>

etc..

Passo 2 - declaração dos valores simbólicos

Sintaxe: =declararparm <nome simbólico> <tipo> <valor>

tipos conhecidos: int , char , float , string

Exemplos

== Declarar as condições de retorno

=declararparm OK int 0

=declararparm ArvoreVazia int 5

== Declarar os valores contidos nos nós

=declararparm CharA char 'a'

26 / 27

(27)

Arquitetura simplificada da nossa abordagem

Programa principal

Módulo em teste

Teste de item de interface Função

de controle específica

Módulo de controle

genérico Diretivas

de teste

Log do teste Módulo de

leitura LERPARM.h

GENERICO.h PRINCIP.h

coordena a realização dos testes; disponibiliza funções genéricas de comparação

TST_ESPC.h

interpreta os comandos genéricos e específicos

*.script

(28)

Construindo módulo controlador de teste...

28 / 27

• Exemplo: TestaArvore.c

– interface: TST_ESPC.h (interface única para todos módulos de teste)

Passo 1 – incluindo as interfaces necessárias

#include "TST_ESPC.H"

#include "generico.h"

#include "lerparm.h"

#include "arvore.h"

interface do módulo sendo testado...

interfaces do arcabouço...

(29)

Construindo módulo controlador de teste...

• Exemplo: TestaArvore.c

Passo 2 – declarando comandos da linguagem de script de teste

/* Nomes dos comandos de teste específicos */

#define CRIAR_ARV_CMD "=criar"

#define INS_DIR_CMD "=insdir"

#define INS_ESQ_CMD "=insesq"

#define IR_PAI_CMD "=irpai"

#define IR_ESQ_CMD "=iresq"

#define IR_DIR_CMD "=irdir"

#define OBTER_VAL_CMD "=obter"

#define DESTROI_CMD "=destruir"

Instrução de pré-processamento para definição de constantes...

(30)

Construindo módulo controlador de teste...

30 / 27

• Exemplo: TestaArvore.c

Passo 3 – implementar função de tratamento e execução do comando de teste

/*****************************************************************

******

*

* $FC Função: Efetuar operações de teste específicas para árvore

*

* $EP Parâmetros

* $P ComandoTeste - String contendo o comando

*

******************************************************************

*****/

TST_tpCondRet TST_EfetuarComando( char * ComandoTeste ) {

}

completar tratamento de cada comando sequencia de if e else ifs

(31)

Exemplo de fragmento do controlador

if ( strcmp( ComandoTeste , CRIAR_ARV_CMD ) == 0 ) {

/* Testar ARV Adicionar filho à direita */

else if ( strcmp( ComandoTeste , INS_DIR_CMD ) == 0 ) {

NumLidos = LER_LerParametros( "ci" ,

&ValorDado , &CondRetEsperada ) ; if ( NumLidos != 2 )

{

return TST_CondRetParm ; } /* if */

return TST_CompararInt( CondRetEsperada , ARV_InserirDireita( ValorDado ) ,

"Retorno errado inserir a direita." );

} /* fim: Testar ARV Adicionar filho à direita */

(32)

Ago 2008 Arndt von Staa © LES/DI/PUC-Rio 32 / 27

Como usar?

• Forma “big bang”

1. Especificar a interface do módulo

arquivo .h

2. Especificar a linguagem de diretivas de teste, sintaxe

=<nome da função> <lista parâmetros> <condição retorno>

3. Redigir a massa de teste nesta linguagem

serve como uma especificação executável!

4. Redigir o módulo de teste específico 5. Redigir o módulo a ser testado

6. Rever cuidadosamente os cinco artefatos 7. Compilar

usar a biblioteca: ArcaboucoTeste.lib

8. Executar o teste

9. Corrigir até zero falhas observadas pelo teste

(33)

Como usar?

• Forma iterativa

– preparação inicial

1. Especificar parcialmente a interface do módulo a desenvolver 2. Especificar a linguagem de diretivas de teste

3. Redigir parte da massa de teste nesta linguagem 4. Redigir a versão inicial do módulo de teste específico

todos os comandos retornam TST_CondRetNaoImplementado 5. Redigir o enchimento (stub) do módulo a ser testado

todas as funções fazem nada, se necessário retornam um valor neutro 6. Rever cuidadosamente os cinco artefatos

7. Compilar

usar a biblioteca: ArcaboucoTeste.lib 8. Executar o teste

9. Corrigir até que sejam gerados somente erros de comando não implementado

(34)

Ago 2008 Arndt von Staa © LES/DI/PUC-Rio 34 / 27

Como usar?

• Forma iterativa

– iteração

1. Escolher as funções a serem implementadas 2. Rever ou completar a interface do módulo 3. Rever a linguagem de diretivas de teste 4. Redigir a massa de teste nesta linguagem

5. Redigir a parte do módulo de teste específico visando as funções 6. Redigir as funções do módulo a ser testado

7. Rever cuidadosamente os cinco artefatos 8. Compilar

usar a biblioteca: ArcaboucoTeste.lib 9. Executar o teste

10.Corrigir até que esteja tudo correto

sejam gerados somente erros de comando não implementado

(35)

Exemplo prático

• Exemplo \arcabouc\simples

(36)

Ago 2008 Arndt von Staa © LES/DI/PUC-Rio 36 / 27

Quais seriam as vantagens?

• Teste automatizado exige rigor ao escrever

– as especificações das interfaces – o script de teste

– embora alguns não acreditem, rigor é sempre vantagem!

• Facilita o desenvolvimento incremental do módulo

– a cada incremento pode-se retestar integralmente o que já foi feito (teste de regressão)

o que não foi alterado ou acrescido deveria continuar operando tal como esperado

o que foi alterado e acrescido tem o teste ajustado

• A função de teste específico serve como exemplo de uso do

módulo

(37)

Quais seriam as vantagens?

• O script de teste serve como especificação executável do módulo

– apesar de ser uma especificação incompleta e baseada em exemplos, freqüentemente é mais precisa do que

especificações textuais

• Os problemas encontrados são repetíveis, facilitando a depuração

– reduz significativamente o esforço de teste quando se leva em conta a necessidade de reteste após corrigir ou evoluir o

módulo

• Caso a realização do teste gere um arquivo de log

– documenta os laudos dos testes realizados

– assegura a existência da história da evolução durante o teste.

– passa a ser um atestado da qualidade do módulo

(38)

Ago 2008 Arndt von Staa © LES/DI/PUC-Rio 38 / 27

Quais seriam as vantagens?

• O esforço de redação do módulo de teste específico é pouco maior do que o esforço de redação de um controlador de teste manual.

• Através da linguagem de diretivas de teste pode-se realizar testes tão complexos e detalhados quanto se queira.

– casos de teste selecionados segundo critérios de seleção bem definidos

– redigir as diretivas de teste requer monos esforço do que redigir um controlador de teste contendo o roteiro

• Permite documentar os casos de teste

– os títulos ( “==” ) informam a intenção do caso de teste – necessário para que outros possam mantê-los

(39)

Quais seriam as vantagens?

• Permite estabelecer com precisão onde o debugger deve ser ativado.

– permite longos processamentos necessário para estabelecer o contexto

– comando de teste genérico: =breakpoint

• Pode ser combinado com funções de teste complexas disparadas por diretivas

• Reduz o estresse do desenvolvedor

– é possível particionar o desenvolvimento em etapas – desenvolvimento incremental

– cada qual culminando com um módulo parcial porém corretamente implementado

(40)

Ago 2008 Arndt von Staa © LES/DI/PUC-Rio 40 / 27

E quais seriam as desvantagens?

• O arquivo de diretivas é na realidade um programa

– pode conter defeitos

– se não tomar cuidado, a linguagem e/ou o script de teste tornam-se extensos e complicados

• Ao encontrar uma falha é necessário determinar se é

– defeito no módulo em teste

– defeito no módulo de teste específico – defeito no script de teste

– defeito na especificação do módulo

• A linguagem ad hoc utilizada aqui não permite a redação de subprogramas (por enquanto )

– subprogramas podem ser codificados no módulo de teste específico e disparados por um comando.

(41)

E quais seriam as desvantagens?

• Custo inicial maior

– ganha-se ao retestar, ou seja, sempre 

• Ao alterar um módulo, obriga a evoluir  coevolução

– obviamente o módulo sob teste – o módulo de teste específico

– o script de teste, isso pode tornar-se um problema

se os casos de teste forem mal documentados

se o script de teste for mal organizado

se o script de teste não utilizar parâmetros simbólicos

• Nem sempre é possível utilizar teste automatizado

– exibição (rendering) de figuras, gráficos

– interfaces com os sistemas GUI (ex. windows) – leiaute de janelas

. . .

(42)

42 / 30 LES/DI/PU

PARA CASA: exemplos práticos de uso do arcabouço

Teste manual:

– Exemplo \arcabouc\manual: mostrado como construir um módulo e o respectivo controlador de teste manual

Teste automatizado:

– Exemplo \arcabouc\simples: mostrado como construir um módulo e o respectivo módulo de teste específico utilizando a biblioteca do arcabouço de teste

Comparar a implementação dos dois

– os exemplos tratam de um módulo editor de árvores binárias

VIDE VÍDEOS EM “INSTALAÇÃO E USO DO ARCABOUÇO”

(43)

Aula 05

Teste Automatizado – Parte I e II

Alessandro Garcia LES/DI/PUC-Rio

Março de 2019

Referências

Documentos relacionados

UM ESTUDO DA HISTÓRIA AFRICANA ANTIGA REVELA UMA ANTERIOR DEFINIÇÃO AFRICANA DO SISTEMA DE MELANINA HUMANA COMO UM [INTEIRO] SANTO CORPO PRETO [HOLY BLACK BODY

10° Aplicação de fórmulas: P9 e B13 para tosse ; B13 e B38 para asma ; BP6 e E30 para as regras dolorosas ; ID3 e C6 para transpiração noturna ; B54 e R1 para hipertensão,

Proibida a reprodução parcial ou total sem autorização

Funções Públicas (LTFP), aprovada pela Lei n.º 35/2014, de 20 de junho e do disposto no artigo 11.º da Portaria n.º 125 -A/2019, de 30 de abril, alterada pela Portaria n.º 12

No Amapá as comunidades tradicionais conquistaram a força política para a criação pelo governo estadual do Programa de Desenvolvimento Sustentável do Amapá (PDSA), um programa

principais experiências profissionais durante os últimos 5 anos, indicando: nome e setor de atividade da empresa, cargo e se a empresa integra (i) o grupo econômico do

• A Revolução Industrial corresponde ao processo de industrialização que teve início na segunda metade do.. século XVIII no

Então Ulisses, que todos diziam ser o mais manhoso dos homens, pensou, pensou e teve uma ideia: construir um enorme, um gigantesco cavalo de pau, assente num estrado com rodas para