Upload
valberto-carneiro
View
16
Download
2
Embed Size (px)
Citation preview
1
Testes com Python:Como fazer uma refatoração segura
2
Sobre o apresentadorValberto Vieira Carneiro
Analista de Tecnologia da Informação - IFPBEspecialista em Qualidade e Testes de Software
3
Roteiro
1. Estudo de caso2. Motivação3. Por onde começar?4. Conceitos5. Testes6. Refatoração7. Dicas
4
Estudo de casoMódulo de diárias e passagens do Instituto Federal da
Paraíba
5
Fluxo para solicitar diárias e passagens
6
Fluxo para prestar contas da viagem realizada
7
Meu ObjetivoAplicar melhorias em um sistema que
já está em produção.
SituaçãoPossui uma lista com novos requisitos
Não possui nenhum testePossui manual de usuário
8
Sentimentos
Vamos arrumar a casaPreciso entender como esse código
funciona
Será que vai dar certo?
9
MotivaçãoVamos deixar os testes para depois!
Não temos tempo!Na minha máquina funciona!
10
Testar e refatorar, como fazer?O que testar e o que não testar?
Qual tipo de testes usar?
11
Por onde começar?
Tenha um objetivo bem definidoVerifique se o módulo possui documentação
Faça um teste exploratório nas principais funcionalidadesEstude sobre refatoração
12
Testar
13
O que é teste de software?
Os testes são realizados com a intenção de descobrir defeitos em um sistema. [Myres, 2004]
Os testes de software podem ser usados para mostrar a presença de defeitos, mas nunca para mostrar a ausência
deles. [Dijkstra, 1972]
14
Caixa branca
Código fonteTestes unitários
Caixa preta
RequisitosTestes funcionais
Tipos de testes
15
Práticas que poderiam ajudar?
Fonte: http://www.aniche.com.br/tdd/
TDD - Test Driven Development- Escrever o teste antes de
codificar
16
Requisitos - Regras de negócio -
Segurança - Permissões de usuários -
- Framework- Métodos nativos- Requisitos com baixo valor
de negócio**
O que testar e o que não testar?
17
Dicas
- Comece pelo mais simples!- Evolua os testes gradativamente- As vezes será necessário reorganizar os testes- Teste o fluxo principal- Teste a(s) funcionalidade(s) mais importante(s)
18
Exemplos: teste funcional básico
from django.test import TestCase
class HomologadorTestCase(TestCase): def test_pagina_inicial(self): response = self.client.get('/') self.assertEqual(response.status_code, 200) self.assertContains(response, u"Título da página")
$ ./manage.py test expedicao
19
Exemplos: afirmações- assertEqual(a, b) # a == b- assertNotEqual(a, b) # a <> b- assertTrue(bool) # True- assertFalse(bool) # False- assertRaises(Exception) # raise Exception- assertIn(a, b) # a in b- assertNotIn(a, b) # a not in b- assertGreater(a, b) # a > b- assertLess(a, b) # a < b
20
Exemplos: teste no Suap
from django.test import TestCase
class SuapTestCase(TestCase):
class ExpedicaoTestCase(SuapTestCase):
class HomologadorTestCase(ExpedicaoTestCase): def test_pode_ver_aba_homologador(self): self.logout() successful = self.client.login(username=self.user.username, password='123') self.assertEqual(successful, True) url = "/admin/expedicao/viagemservidor/" response = self.client.get(url, follow=True) self.assertContains(response, u"tab_viagens_homologar")
21
from django.test import TestCase
class SuapTestCase(TestCase):
class ExpedicaoTestCase(SuapTestCase):
class HomologadorTestCase(ExpedicaoTestCase): def test_pode_ver_aba_homologador(self): self.logout() successful = self.client.login(username=self.user.username, password='123') self.assertEqual(successful, True) url = "/admin/expedicao/viagemservidor/" response = self.client.get(url, follow=True) self.assertContains(response, u"tab_viagens_homologar")
Exemplos: teste no Suap
22
from django.test import TestCaseclass SuapTestCase(TestCase):class ExpedicaoTestCase(SuapTestCase):
class HomologadorTestCase(ExpedicaoTestCase): def acessar_como(self, user): self.logout() successful = self.client.login(username=user.username, password='123') self.assertEqual(successful, True)
def test_pode_ver_aba_homologador(self): self.acessar_como(self.homologador) url = "/admin/expedicao/viagemservidor/" response = self.client.get(url, follow=True) self.assertContains(response, u"tab_viagens_homologar")
Exemplos: teste no Suap
23
from django.test import TestCaseclass SuapTestCase(TestCase):class ExpedicaoTestCase(SuapTestCase):
class HomologadorTestCase(ExpedicaoTestCase): def acessar_como(self, user): self.logout() successful = self.client.login(username=user.username, password='123') self.assertEqual(successful, True)
def test_pode_ver_aba_homologador(self): self.acessar_como(self.homologador) url = "/admin/expedicao/viagemservidor/" response = self.client.get(url, follow=True) self.assertContains(response, u"tab_viagens_homologar")
Exemplos: teste no Suap
24
from django.test import TestCaseclass SuapTestCase(TestCase):class ExpedicaoTestCase(SuapTestCase):
class HomologadorTestCase(ExpedicaoTestCase): def acessar_como(self, user): def test_pode_ver_aba_homologador(self): self.acessar_como(self.homologador) url = "/admin/expedicao/viagemservidor/" response = self.client.get(url, follow=True) self.assertContains(response, u"tab_viagens_homologar")
Exemplos: teste no Suap
25
from django.test import TestCaseclass SuapTestCase(TestCase):class ExpedicaoTestCase(SuapTestCase):
class HomologadorTestCase(ExpedicaoTestCase): def acessar_como(self, user): def test_pode_ver_aba_homologador(self): self.acessar_como(self.homologador) url = "/admin/expedicao/viagemservidor/" response = self.client.get(url, follow=True) self.assertContains(response, u"tab_viagens_homologar")
Exemplos: teste no SuapChangelist {{ app_label }}_{{ model_name }}_changelistAdd {{ app_label }}_{{ model_name }}_add /addHistory {{ app_label }}_{{ model_name }}_history /id/historyDelete {{ app_label }}_{{ model_name }}_delete /id/deleteChange {{ app_label }}_{{ model_name }}_change /id
'expedicao_viagemservidor_changelist' '/admin/expedicao/viagemservidor/'
26
from django.test import TestCaseclass SuapTestCase(TestCase):class ExpedicaoTestCase(SuapTestCase):
class HomologadorTestCase(ExpedicaoTestCase): def acessar_como(self, user): def get_changelist_page(cls): ... def test_pode_ver_aba_homologador(self): self.acessar_como(self.homologador) response = self.get_changelist_page(models.ViagemServidor) self.assertContains(response, u"tab_viagens_homologar")
Exemplos: teste no Suap
27
from django.test import TestCaseclass SuapTestCase(TestCase):class ExpedicaoTestCase(SuapTestCase):
class HomologadorTestCase(ExpedicaoTestCase): def acessar_como(self, user): def get_changelist_page(cls): def test_pode_ver_aba_homologador(self): self.acessar_como(self.homologador) response = self.get_changelist_page(models.ViagemServidor) self.assertContains(response, u"tab_viagens_homologar")
Exemplos: teste no Suap
28
from django.test import TestCaseclass SuapTestCase(TestCase):class ExpedicaoTestCase(SuapTestCase):
class HomologadorTestCase(ExpedicaoTestCase): def acessar_como(self, user): def get_changelist_page(cls): def test_pode_ver_aba_homologador(self): self.acessar_como(self.homologador) response = self.get_changelist_page(models.ViagemServidor) self.assertContains(response, u"tab_viagens_homologar") # self.logout() # successful = self.client.login(username=self.user.username, password='123') # self.assertEqual(successful, True) # url = "/admin/expedicao/viagemservidor/" # response = self.client.post(url, follow=True) # self.assertContains(response, u"tab_viagens_homologar")
Exemplos: teste no Suap
29
Refatorar
30
Refatorar é…
um processo disciplinado de alterar o código de um sistema sem alterar o seu comportamento observável
buscando melhorar a estrutura interna e minimizando a chance de introdução de novas falhas.
(Martin Fowler, Kent Beck)
Exige experiência e envolve uma grande responsabilidade.
31
Refatorar não é…
Alterar o código adicionando novos recursosReescrever ou substituir grande partes do código
32
Etapas
1. Detecção de oportunidades de refactoring2. Decisão do Uso (custos e benefícios)3. Preparação da avaliação4. Execução de um dado refactoring5. Avaliação
33
Etapa 1: Detecção de oportunidades de refactoring
Code smellé como se aquele trecho de código cheirasse e você sabe que precisa ser refatorado
Problemas mais comunscódigo duplicado | método longo | classes grandes
comandos switch | campo temporário classe ociosa | comentários
pylint
34
Exemplos: detecção dos pontos de melhoria
# TODO refactory: - Comentário longo.- Comentário desnecessário.- Comentário não condiz com o objetivo do método.- Imports não utilizados.- Método/código duplicado comum.utils.get_funcionario.- Linhas com mais de 80 caracteres.- Método muito extenso.- Número mágico.- Nome muito grande para uma variável (23 caracteres).
35
Exemplos: detecção dos pontos de melhoria
# TODO refactory: - Comentário longo.- Comentário desnecessário.- Comentário não condiz com o objetivo do método.- Imports não utilizados.- Método/código duplicado comum.utils.get_funcionario.- Linhas com mais de 80 caracteres.- Método muito extenso.- Número mágico.- Nome muito grande para uma variável (23 caracteres).
# Cria o relatório referente a esta viagemrelatorio = RelatorioViagem(viagem=viagem)relatorio.save()
36
Exemplos: detecção dos pontos de melhoria
# TODO refactory: - Comentário longo.- Comentário desnecessário.- Comentário não condiz com o objetivo do método.- Imports não utilizados.- Método/código duplicado comum.utils.get_funcionario.- Linhas com mais de 80 caracteres.- Método muito extenso.- Número mágico.- Nome muito grande para uma variável (23 caracteres).
if (data.isoweekday() >= 6):
37
Exemplos: detecção dos pontos de melhoria
# TODO refactory: - Comentário longo.- Comentário desnecessário.- Comentário não condiz com o objetivo do método.- Imports não utilizados.- Método/código duplicado comum.utils.get_funcionario.- Linhas com mais de 80 caracteres.- Método muito extenso.- Número mágico.- Nome muito grande para uma variável (23 caracteres).
SABADO, DOMINGO = 6, 7if data.isoweekday() in [SABADO, DOMINGO]:
38
Exemplos: detecção dos pontos de melhoria
# TODO refactory: - Aquivo não tem limitador de extensão nem de tamanho.- Extrair consulta para um método.- Nomenclatura não exite mais.- Condições complexas demais. Substituir por método:
is_empty(nome_campo).- Substituir condições negativas por uma afirmativas.
39
Exemplos: detecção dos pontos de melhoria
# TODO refactory: - Aquivo não tem limitador de extensão nem de tamanho.- Extrair consulta para um método.- Nomenclatura não exite mais.- Condições complexas demais. Substituir por método:
is_empty(nome_campo).- Substituir condições negativas por uma afirmativas.
arquivo = models.FileField( upload_to='/comprovantes/', verbose_name=u'Comprovante')
40
Exemplos: detecção dos pontos de melhoria
# TODO refactory: - Aquivo não tem limitador de extensão nem de tamanho.- Extrair consulta para um método.- Nomenclatura não exite mais.- Condições complexas demais. Substituir por método:
is_empty(nome_campo).- Substituir condições negativas por uma afirmativas.
arquivo = models.FileField( upload_to='/comprovantes/', verbose_name=u'Comprovante', help_text=u'Tamanho máximo 2MB.', validators=[FileValidator()])
41
Exemplos: detecção dos pontos de melhoria
# TODO refactory: - Aquivo não tem limitador de extensão nem de tamanho.- Extrair consulta para um método.- Nomenclatura não exite mais.- Condições complexas demais. Substituir por método:
is_empty(nome_campo).- Substituir condições negativas por uma afirmativas.
historico = HistoricoSolicitacao.objects. filter(viagem=self). order_by('-data'). all()[0]historico = self.historicos.latest('data')
42
Exemplos: detecção dos pontos de melhoria
# TODO refactory: - Aquivo não tem limitador de extensão nem de tamanho.- Extrair consulta para um método.- Nomenclatura não exite mais.- Condições complexas demais. Substituir por método:
is_empty(nome_campo).- Substituir condições negativas por uma afirmativas.
def ultimo_historico(): return self.historicos.latest('data')
historico = ultimo_historico()
43
Etapa 2: Decisão do Uso (custos e benefícios)
Custos e benefíciosTempo e esforço necessários valerão a pena?
Decisão não automatizadaCódigo duplicado:
Extrair Método, Extrair Classe, Subir Método naHierarquia ou Substituir Algoritmo
44
Etapa 3: Preparação da avaliação
Desenvolver testesGarantir que o comportamento do sistema seja mantido
Decidir qual tipo de testesTestes funcionais
Caixa preta
45
Dicas
Crie grupos de funções que deseja testar- perfil de usuário (testar acesso e negação de acesso)
- fluxo de negócio- regras de negócio (validações)
46
Exemplos: Preparação da avaliação
class ExpedicaoTestCase(SuapTestCase): Testes Tempoclass AdministradorTestCase(ExpedicaoTestCase): | 4 | 00:00:38 |class ExecutorTestCase(ExpedicaoTestCase): | 4 | 00:00:02 |class HomologadorTestCase(ExpedicaoTestCase): | 1 | 00:00:00 |class AutorizadorUOTestCase(ExpedicaoTestCase): | 2 | 00:00:01 |class ChefeTestCase(ExpedicaoTestCase): | 5 | 00:00:03 |class SubstitutoTestCase(ExpedicaoTestCase): | 4 | 00:00:03 |class ViagemServidorTestCase(ExpedicaoTestCase): | 14 | 00:00:11 |class RelatorioViagemTestCase(ExpedicaoTestCase):| 7 | 00:00:09 |
TOTAL: 41 | 00:01:12 |Cobertura 72%
$ pip install coverage
47
Etapa 4: Execução de um dado refactoring
Aplicar as refatoraçõesObjetivo é eliminar o code smell
Qual o momento certo?Atividade contínua, integrada ao desenvolvimento:
- Acrescentar uma função- Consertar uma falha
- Revisar o código
48
Exemplos: Aplicar refatorações
def relatorio_detail(request, id): title = u'Relatório do Servidor' usuario = request.user try: relatorio = RelatorioViagem.objects.get(id=id) except RelatorioViagem.DoesNotExist: return HttpResponseBadRequest(u'O relatório não existe!') if not (eh_dono_viagem(request, relatorio.viagem.id) or \ eh_ordenador(request) or \ eh_executor(request) or \ usuario.eh_chefe_do_setor(usuario.setor)): raise PermissionDenied
return locals()
49
Exemplos: Aplicar refatorações
def relatorio_detail(request, id): title = u'Relatório do Servidor' usuario = request.user try: relatorio = RelatorioViagem.objects.get(id=id) except RelatorioViagem.DoesNotExist: return HttpResponseBadRequest(u'O relatório não existe!') if not (eh_dono_viagem(request, relatorio.viagem.id) or \ eh_ordenador(request) or \ eh_executor(request) or \ usuario.eh_chefe_do_setor(usuario.servidor.setor)): raise PermissionDenied
return locals()
relatorio = get_object_or_404(RelatorioViagem, pk=id)
50
Exemplos: Aplicar refatorações
def relatorio_detail(request, id): title = u'Relatório do Servidor' usuario = request.user relatorio = get_object_or_404(RelatorioViagem, pk=id) if not (eh_dono_viagem(request, relatorio.viagem.id) or \ eh_ordenador(request) or \ eh_executor(request) or \ usuario.eh_chefe_do_setor(usuario.setor)): raise PermissionDenied
return locals()
51
Exemplos: Aplicar refatorações
def relatorio_detail(request, id): title = u'Relatório do Servidor' usuario = request.user relatorio = get_object_or_404(RelatorioViagem, pk=id) if not (eh_dono_viagem(request, relatorio.viagem.id) or \ eh_ordenador(request) or \ eh_executor(request) or \ usuario.eh_chefe_do_setor(usuario.setor)): raise PermissionDenied
return locals()
regra = regras.RelatorioViagemRegras(relatorio) if not regra.pode_ver(request.user): raise PermissionDenied
52
Exemplos: Aplicar refatorações
def relatorio_detail(request, id): title = u'Relatório do Servidor' relatorio = get_object_or_404(RelatorioViagem, pk=id) regra = regras.RelatorioViagemRegras(relatorio) if not regra.pode_ver(request.user): raise PermissionDenied
return locals()
Etapa 5: Avaliação
O comportamento é o mesmo?Analisar resultado dos testes
Realizar análise cognitivaBuscar novo code smell
53
54
Fonte: CARNEIRO, 2003
55
Perguntas
56
Referências- CARNEIRO, Glauco de F. Usando medição de código fonte para refactoring. Abril de 2003. 123 p. Dissertação (Mestrado
Profissional em Redes de Computadores) - Universidade Salvador, Salvador, 2003.- Dijkstra, E. W. "The Humble Programmer". Communications of the ACM 15 (10): 859–866, 1972.- Entendendo Testes de Software https://willianjusten.com.br/entendendo-testes-de-software/- FOWLER, Martin et al. Refatoração: Aperfeiçoando o Projeto de Código Existente. trad.Acauan Fernandes – Porto Alegre:
Bookman, 2004.- Improve Your Python: Understanding Unit Testing https://jeffknupp.com/blog/- List for code-quality tools related to Python https://mail.python.org/mailman/listinfo/code-quality- Myres , G. F. “The Art of Software Testing”. Ed. John Wiley & Sons, Inc. New Jersey, 2004.- O que é e o que não é refatoração – de acordo com Kent Beck e Martin Fowler
https://dzone.com/articles/what-refactoring-and-what-it-0- Os 10 princípios dos Testes de Software http://gtsw.blogspot.com.br/2007/12/os-princpios-dos-testes-de-software.html- Refactoring Patterns https://dzone.com/refcardz/refactoring-patterns- Review of Python Static Analysis Tools http://blog.codacy.com/2016/01/08/review-of-python-static-analysis-tools/- Start Here: Introductions to Python Testing Frameworks http://pythontesting.net/start-here/- Testes e Refatoração http://pt.slideshare.net/jeveaux/testes-e-refatorao-presentation-622966- Writing unit tests in Python: How do I start?
http://stackoverflow.com/questions/3371255/writing-unit-tests-in-python-how-do-i-start