Upload
rodrigo-paes
View
520
Download
1
Embed Size (px)
DESCRIPTION
Fala principalmente sobre a cobertura de testes
Citation preview
Cobertura de testes
Ubuntu release
oUbuntu 12.04 LTS
oA primeira release estável só começou no .04
o Logo depois do lançamento
oBugs, bugs e mais bugs
Não é assim com quase todos os software
o Por que que os desenvolvedores lançam uma release com bugs?
oParece que o trabalho dos testadores falhou ao não encontrar os erros não é?
oParece que algumas partes do código foram esquecidas pelos desenvolvedores e testadores
Cobertura
o Iremos ver algumas técnicas que ajudam a identificar o quanto executamos do nosso código fonte sob teste
oCuidado
http://www.linozemtseva.com/research/2014/icse/coverage/coverage_paper.pdf
“Our results suggest that coverage, while useful for identifying under-tested parts of a program, should not be used as a quality target because it is not a good indicator of test suite efectiveness.”Inozemtseva, Laura, and Reid Holmes. "Coverage Is Not
Strongly Correlated With Test Suite Effectiveness.“, ICSE 2014
Quanto teste é suficiente?
oA gente começa a testar, gasta um bom tempo e se sente confiante que fez um bom trabalho
Mas ...
oDe repente alguma coisa esquisita acontece e o nosso software falha!
oNa prática, esquecemos de testar alguma parte do nosso domínio da entrada
Ideia
oA ideia é termos algumas métricas que nos digam, por exemplo:
oVocê está indo bem, seu score de cobertura é 14/100
oOu ainda:
o Você não executou a linhas 35, 70 e 195 da sua classe Queue
Voltando um pouco para discutir a partição do domínio
oNão é viável testar com todas as possíveis entradas
Domínio da entrada Contra-domínio (saídas)
Suponha ainda
oMesmo que você abstraia e considere classes do seu domínio de entrada
o Suponha também que você tem acesso ao:
oCódigo do software que você irá testar
oEspecificações
oE tudo o mais que você precise para criar as melhores partições possíveis do seu domínio da entrada
Logo ...
o Você pega um ponto da partição, testa, analisa o resultado obtido e se for igual ao esperado, ok! O sistema se comportou corretamente
o Mas em alguns casos, o sistema irá falhar, mesmo para pontos diferentes dentro de uma mesma partição
oOu seja ... Descobrimos uma outra partição dentro da partição que a gente achava que era a melhor
o ... E culpamos a escolha da partição
o Só que esse processo vira um loop ... É difícil encontrar boas partições ... E pior ainda é difícil medir (cobertura do domínio ???)
Cobertura
oNa prática, a gente faz um pouco diferente e acaba particionando também o domínio ... mas como consequência
oMais fácil com exemplo ... Próximo slide
Cobertura de funções
oVocê terá 100% de cobertura de funções se todas as funções do seu sistema forem executadas durante os testes
o Por exemplo, você pode fazer um teste que executa, por exemplo, 181 das 250 funções do seu código
o Essa métrica 181/250 é uma das métricas de cobertura
Ou seja,
oVocê teve que escolher elementos do seu domínio que exercitassem as funções do seu código
oAs funções geraram saídas (contra-domínio)
oOu seja, ao tentar cobrir as funções, você cobriu uma parte do domínio e do contra-domínio de valores
oMas o foco foi na cobertura das funções
oE não em tentar cobrir todo o domínio
Existem várias formas de medir a cobertura
oCobertura de função
oCobertura de comandos
oCobertura de linhas
oCobertura de ramos
oCobertura de loops
oCobertura Modified Condition/Decision Condition
Iremos explorar cada uma delas ...
o ... mas primeiro ...
o ... Um exemplo
o Uma árvore de busca binária
find (key)
Agora vamos incrementar um pouco
oUma splay tree (com algumas simplificações)
oÁrvore de busca binária
oAuto ajustável
oElementos mais acessados são acessados mais rápidos
o Eles sobem para o topo da árvore
Ideia
oCombinar as operações normais (insert e find) com uma nova operação
oSplay
o Responsável por levar para o topo da árvore o elemento inserido ou buscado
o Ela faz uso da rotação de árvores
Como rotacionar uma árvore?
oMudar a estrutura da árvore sem interferir na ordem dos elementos
Consequências da rotação
oVocê só poderá rotacionar p se ele tiver um pai
o Se P já é filho esquerdo de Q, não dá pra rodar pra esquerda, por exemplo
o Se Q tiver um pai, ele precisará ser atualizado para apontar para P e P para ser filho ele
Primeira tarefa
o Implementar
o right_rotate(self, node)
o left_rotate(self, node)
28
O splay (simplificado)
o Iremos rotacionar o nó até ele chegar na raiz
o Por exemplo:
8
4 9
63
5 7
8
4 9
53
7
6
8
5 9
4
7
6
3
leftright 5
4 8
3
7
6
right
9
Escreva os seus testes
Cobertura dos testes que você fez
oVamos dar uma olhada na cobertura dos testes que você fez
o Para isso, iremos usar uma ferramenta do Python
oO Python Coverage irá contar quantos comandos foram executados em relação ao total
oCobertura de comandos
Python coverage (http://nedbatchelder.com/code/coverage/)
o pip install coverage
o Verifique se a pasta Script dentro do python está no seu path, se não estiver coloque. Por exemplo, no meu computador essa pasta é: D:\python3.3\Scripts
o Vá para o diretório que você fez o caso de teste
o coverage run my_tests.py
o Troque my_tests.py pelo nome do seu script de testes
o coverage html
o Ele vai gerar um diretório chamado htmlcov
o Abra o arquivo index.html para ver os resultados
o Apagar resultados anteriores: coverage erase
o Resultado em outro formato: coverage report -m
Rodou os testes instrumentados com a ferramenta de cobertura?
o Provavelmente a cobertura não deu 100%
o Espere .... não codifique mais testes para obter os 100% ainda
o Identifique por que os comandos não foram executados?
o Era um código que você julgou que não precisava ser testado?
o Você não conseguiu pensar nessa situação?
oUé ... ele deveria ter executado esse comando
O que ele nos diz?
oO teste de cobertura nos diz o que não testamos
o Ele deve sempre gerar uma reflexão da causa do “esquecimento”
oNão saia tentando obter 100% de cobertura sem fazer essa reflexão
Melhore os seus testes
o Se você julgou que esqueceu de testar algumas situações importantes
oMelhore os seus casos de teste
100%
oVocê obteve 100% de cobertura de comandos
o Logo seu código está livre de bugs, certo?
oClaro que não
oAinda podem existir muitos membros do seu domínio de teste que podem disparar um bug
Exemplo
import math
def is_prime(number):
if number <= 1 or (number % 2) == 0:
return False
for check in range(3, int(math.sqrt(number))):
if number % check == 0:
return False
return True
Testes
import unittest
from prime import is_prime
class MyTestCase(unittest.TestCase):
def test_is_prime(self):
self.assertFalse(is_prime(1))
self.assertFalse(is_prime(2))
self.assertTrue(is_prime(3))
self.assertFalse(is_prime(4))
self.assertTrue(is_prime(5))
self.assertFalse(is_prime(20))
self.assertFalse(is_prime(21))
self.assertFalse(is_prime(22))
self.assertTrue(is_prime(23))
self.assertFalse(is_prime(24))
Porém ...
o A nossa função possui uma falta
o Tente identificá-la
o Quando descobrir, modifique o caso de teste para que a falta seja exercitada e se transforme em uma falha, que será “pega” pelo seu caso de teste
o Só depois, corrija a falta
O teste que irá exercitar a falta
import unittest
from prime import is_prime
class MyTestCase(unittest.TestCase):
def test_is_prime(self):
self.assertFalse(is_prime(9))
self.assertFalse(is_prime(25))
A falta
o for check in range(3, int(math.sqrt(number))):
oDeveria ser:
o for check in range(3, int(math.sqrt(number))+1):
oNa função range, o primeiro parâmetro é inclusivo e o segundo exclusivo. Ou seja, se vc a chamar range(1,4), ele irá gerar o range: 1,2,3
Mas o que aconteceu?
o Tínhamos 100% de cobertura de comandos e ainda assim encontramos uma falta ?!
oA cobertura de comandos ainda deixa passar várias situações
oO comando pode executar mas retorna um valor errado
oO loop pode executar, mas executa um número errado de iterações
o ...
Possíveis conclusões sobre esse processo
oO fato de ter 100% cobertura de comandos não significa que o software está livre de faltas
oSignifica apenas que ele executou todos os comandos
oQuando deixamos de cobrir algum comando, o que realmente isso significa?
oQue temos que escrever um casos de teste para executar esse comando?
Possíveis conclusões
oUma conclusão melhor seria
oSignifica que a gente não pensou no problema direito
oÉ melhor que a gente investigue porque que você não pensou naquele caso do que simplesmente sair escrevendo o casos de teste para executar o comando que foi deixado de lado
Métricas de cobertura
Quantas são?
o Em [1], foram descritas 101 maneiras diferentes de medir a cobertura
oMas vamos simplificar e falar apenas de 6 delas, consideradas as principais
[1] Kaner, Cem. "Software negligence and testing coverage." Proceedings of STAR 96 (1996): 313.
Cobertura de comandos
o Já vimos essa métrica através da ferramenta Python Coverage
oVamos só detalhar um pouco mais
o Suponha:if x == 0: y += 1if y == 0: x += 1
Se chamarmos com: (x= 0, y= -1)
obteremos 100% dos comandos executados(x=20, y=20)
2/4 executados, ou seja, 50%
Cobertura de linhas
oMuito parecido com a cobertura de comandos
oMas conta as linhas ao invés de comandos
o Logo, se o seu código tiver um comando por linha, as métricas serão iguais
Cobertura de linhas
oCódigo em C:
o Independentemente dos valores de a e b obteremos 100% de cobertura de linha, mesmo que nem todos os comandos tenham sido executados
int do_something(int a, int b){ int c; if (a>b) c=a;else c=b; printf("%d\n",c);}
Quiz
1. Baixe esse código: http://goo.gl/M07GMb
2. Escreva um teste unitárioclass MyTestCase(unittest.TestCase):
def test_stats(self):
l = [31]
my_map = stats(l)
self.assertEqual(31,my_map["min"])
self.assertEqual(31,my_map["max"])
self.assertEqual(31,my_map["median"])
self.assertEqual([31],my_map["modes"])
3. Modifique o teste unitário para obter 100% de cobertura de comandos
Quiz (continuação)
4. Insira uma falta na função “stats” de forma que a falta passe desapercebida pelo teste que você tinha escrito
5. Escreva outro teste que capture a falta que você inseriu
Uma possível solução
o Introdução da falta
oColocar um valor absoluto, assim irá ignorar os valores negativos
oEssa falta não será detectada pelo teste já feito
oVeja a linha 6: https://gist.github.com/r0drigopaes/401b298741c2fa88f70a
o Testes para pegar a falta
oVeja a linha 32: https://gist.github.com/r0drigopaes/be13f8557dd604b57b01
Cobertura de ramos
o É a métrica que se preocupa com os ramos da execução do seu código
o Por exemplo, para obter 100% no ramo ...
if (x == 0): y = y +1
o ... ele precisaria ser executado em ambas as possibilidades
oCom x igual a 0 e com x diferente de 0
54
Cobertura de ramos
def foo(x,y):
if x == 0:
y += 1
if y == 0:
x += 1
X == 0
Y == 0
Y += 1
X+=1
...4 ramos no total
foo(0,1) ?
foo(0,-1) ?
Cobertura de ramos (python)
o coverage run –branch seuarquivo.py
o coverage html
def foo(x,y): if x == 0: y += 1 if y == 0: x +=1 foo(0,1)
Como o coverage calcula?
o coverage=execuções reais(er)/oportunidades de execução(oe)
o oe = statements + branches
o er = oe – (missing + partial)
o Exemplo:
o oe = 7 + 4 = 11 :: er = 11 – (2 + 2) = 7 :: logo
o Coverage = 7/11 = 64%
def foo(x,y): if x == 0: y += 1 if y == 0: x +=1 foo(0,1)
Ainda sobre o cálculo
o oe = 8 + 4 = 12
o er = 12 – (3+2) =7
oCoverage = 7/12 = 58%def foo(x,y): if x == 0: y += 1 x += 2 if y == 0: x +=1 foo(0,1)
Informações úteis
oNúmero da linha que não foi executada no ramo
oNesse caso, o primeiro if nunca foi verdadeiro, pois não “pulou” para a linha 5
oE o segundo também não, pois não pulou para a linha 8
Cobertura de loops
oConta se o “corpo” de um loop foi executado pelo menos
o0 vezes
o1 vez
oMais de uma vez
oA forma específica de contagem pode variar
oO coverage não conta essa métrica
Cobertura de loops
for loop in open("file"):
process(line)
o Para obter cobertura complete de loop:
oArquivo sem nenhuma linha
oArquivo com uma linha
oArquivo com pelo menos 2 linhas
Complexidade Ciclomática
o Ideia: medir a quantidade de decisões no código-fonte
oQuanto menos decisões, mais simples de manter
oTambém traz a noção de caminhos básicos independentes
Thomas J. Mccabe; A Complexity Measure. IEEE Transactions on Software Engineering, 1976
http://dx.doi.org/10.1109/TSE.1976.233837
Complexidade Ciclomática
oVer o programa como um grafo
oOnde
oe : arestas
on: nós
op: componentes conectados
Complexidade ciclomática
o Sequência
oV = 1 – 2 + 2
oV = 1
A = 5+3;B = A+1;
1 caminho
Complexidade ciclomática
oDecisão
oV = 4 – 4 + 2
oV = 2
Com uma decisão, aumentamos um na complexidade
if (a>b){ printf(“x”);}else{ printf(“y”);}b = a+b;
2 caminhos
Complexidade ciclomática
oDecisão
oV = 12 – 10 +2
oV = 4
3 decisões.
4 caminhos
Teste baseado na complexidade ciclomática
oBasis Path
oMas vamos explicar com um exemplo ...
Euclideseuclid(int m, int n){ int r; if(n>m){ r = m; m = n; n = r; } r = m % n; while (r!=0){ m = n; n = r; r = m % n; } return n;}
012345678910111213
0
1
5
13
12
11
10
9
8
7
6
4
3
2
V = 15 – 14 + 2V = 3
Euclideseuclid(int m, int n){ int r; if(n>m){ r = m; m = n; n = r; } r = m % n; while (r!=0){ m = n; n = r; r = m % n; } return n;}
012345678910111213
0
1
5
13
12
11
10
9
8
7
6
4
3
2
Euclides
oQualquer caminho pode ser expresso como uma combinação linear de um “basis path”
o Exemplo:
o0 1 2 3 4 5 6 7 8 9 10 7 8 9 10 11 12 13
o= B2 – 2*B1 + 2*B3
0
1
5
13
12
11
10
9
8
7
6
4
3
2
o [Mostrar as operações algébricas com matrizes]
MC/DC
oModified Condition Decision Coverage
o Essa é mais fácil mostrando um exemplo:
o if a or (b and c): print(“It is true!!”)
oOu seja, a gente vai imprimir se a for verdade ou então b ou c
MC/DC
A B C A or (B and C)
True True True True
True True False True
True False True True
True False False True
False True True True
False True False False
False False True False
False False False False
Passo 1: em cada condição usada na decisão, procurar por aquelas que afetam a saída de forma independente
a) Nesse caso, “fixe B e C” e mude o valor de A.
b) Ele altera a saída? Se sim, esses dois seriam 2 casos de testesa) T, T, T e F, T, T
MC/DC
A B C A or (B and C)
True True True True
True True False True
True False True True
True False False True
False True True True
False True False False
False False True False
False False False False
Passo 1: em cada condição usada na decisão, procurar por aquelas que afetam a saída de forma independente
a) Agora vamos tentar com B. Fixe A (True) e C(True) e varie B.
b) Afetou a saída? Não. Tente outra possibilidadea) A(False), B(True),
C(False)c) Altere somente B, de novo
a) A(False), B(False), C(False)
d) Alterou?a) Simb) F, T, F e F, F, F
MC/DC
A B C A or (B and C)
True True True True
True True False True
True False True True
True False False True
False True True True
False True False False
False False True False
False False False False
A mesma coisa para C
a) A(False), B(True), C(True)b) Altere para
a) A(False), B(True), C(False)
c) F, T,T e F, T, F
MC/DC
o Teríamos os seguintes casos de teste:
oT, T, T e F, T, T
oF, T, F e F, F, F
oF, T,T e F, T, F
oT, T, T e F, T, T
oF, F, F
3 casos de teste cobririam 100% mc/dc
MC/DC
oUsado em partes específicas de sistemas críticos
oNão conheço nenhuma ferramenta para python