Testes com python: como fazer uma refatoração segura

56
1 Testes com Python: Como fazer uma refatoração segura

Transcript of Testes com python: como fazer uma refatoração segura

Page 1: Testes com python: como fazer uma refatoração segura

1

Testes com Python:Como fazer uma refatoração segura

Page 2: 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

Page 3: Testes com python: como fazer uma refatoração segura

3

Roteiro

1. Estudo de caso2. Motivação3. Por onde começar?4. Conceitos5. Testes6. Refatoração7. Dicas

Page 4: Testes com python: como fazer uma refatoração segura

4

Estudo de casoMódulo de diárias e passagens do Instituto Federal da

Paraíba

Page 5: Testes com python: como fazer uma refatoração segura

5

Fluxo para solicitar diárias e passagens

Page 6: Testes com python: como fazer uma refatoração segura

6

Fluxo para prestar contas da viagem realizada

Page 7: Testes com python: como fazer uma refatoração segura

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

Page 8: Testes com python: como fazer uma refatoração segura

8

Sentimentos

Vamos arrumar a casaPreciso entender como esse código

funciona

Será que vai dar certo?

Page 9: Testes com python: como fazer uma refatoração segura

9

MotivaçãoVamos deixar os testes para depois!

Não temos tempo!Na minha máquina funciona!

Page 10: Testes com python: como fazer uma refatoração segura

10

Testar e refatorar, como fazer?O que testar e o que não testar?

Qual tipo de testes usar?

Page 11: Testes com python: como fazer uma refatoração segura

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

Page 12: Testes com python: como fazer uma refatoração segura

12

Testar

Page 13: Testes com python: como fazer uma refatoração segura

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]

Page 14: Testes com python: como fazer uma refatoração segura

14

Caixa branca

Código fonteTestes unitários

Caixa preta

RequisitosTestes funcionais

Tipos de testes

Page 15: Testes com python: como fazer uma refatoração segura

15

Práticas que poderiam ajudar?

Fonte: http://www.aniche.com.br/tdd/

TDD - Test Driven Development- Escrever o teste antes de

codificar

Page 16: Testes com python: como fazer uma refatoração segura

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?

Page 17: Testes com python: como fazer uma refatoração segura

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)

Page 18: Testes com python: como fazer uma refatoração segura

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

Page 19: Testes com python: como fazer uma refatoração segura

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

Page 20: Testes com python: como fazer uma refatoração segura

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")

Page 21: Testes com python: como fazer uma refatoração segura

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

Page 22: Testes com python: como fazer uma refatoração segura

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

Page 23: Testes com python: como fazer uma refatoração segura

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

Page 24: Testes com python: como fazer uma refatoração segura

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

Page 25: Testes com python: como fazer uma refatoração segura

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/'

Page 26: Testes com python: como fazer uma refatoração segura

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

Page 27: Testes com python: como fazer uma refatoração segura

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

Page 28: Testes com python: como fazer uma refatoração segura

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

Page 29: Testes com python: como fazer uma refatoração segura

29

Refatorar

Page 30: Testes com python: como fazer uma refatoração segura

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.

Page 31: Testes com python: como fazer uma refatoração segura

31

Refatorar não é…

Alterar o código adicionando novos recursosReescrever ou substituir grande partes do código

Page 32: Testes com python: como fazer uma refatoração segura

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

Page 33: Testes com python: como fazer uma refatoração segura

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

Page 34: Testes com python: como fazer uma refatoração segura

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).

Page 35: Testes com python: como fazer uma refatoração segura

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()

Page 36: Testes com python: como fazer uma refatoração segura

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):

Page 37: Testes com python: como fazer uma refatoração segura

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]:

Page 38: Testes com python: como fazer uma refatoração segura

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.

Page 39: Testes com python: como fazer uma refatoração segura

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')

Page 40: Testes com python: como fazer uma refatoração segura

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()])

Page 41: Testes com python: como fazer uma refatoração segura

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')

Page 42: Testes com python: como fazer uma refatoração segura

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()

Page 43: Testes com python: como fazer uma refatoração segura

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

Page 44: Testes com python: como fazer uma refatoração segura

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

Page 45: Testes com python: como fazer uma refatoração segura

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)

Page 46: Testes com python: como fazer uma refatoração segura

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

Page 47: Testes com python: como fazer uma refatoração segura

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

Page 48: Testes com python: como fazer uma refatoração segura

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()

Page 49: Testes com python: como fazer uma refatoração segura

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)

Page 50: Testes com python: como fazer uma refatoração segura

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()

Page 51: Testes com python: como fazer uma refatoração segura

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

Page 52: Testes com python: como fazer uma refatoração segura

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()

Page 53: Testes com python: como fazer uma refatoração segura

Etapa 5: Avaliação

O comportamento é o mesmo?Analisar resultado dos testes

Realizar análise cognitivaBuscar novo code smell

53

Page 54: Testes com python: como fazer uma refatoração segura

54

Fonte: CARNEIRO, 2003

Page 55: Testes com python: como fazer uma refatoração segura

55

Perguntas

Page 56: Testes com python: como fazer uma refatoração segura

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