Tdd em django sem desculpas versao final

5.392 visualizações

Publicada em

Versao Apresentada no TDC2010. Mais completa que a versao do fisl.

6 comentários
16 gostaram
Estatísticas
Notas
  • PErfeito!
    Bom pessoal...quem teve um erro quando executou o manage.py runtester igual a esse: ValueError: signal only works in main thread Segue a correção: https://github.com/ebas/django/commit/aede54256a78c45a8c51812ab35aebc978adddbf
       Responder 
    Tem certeza que deseja  Sim  Não
    Insira sua mensagem aqui
  • Pow kra, muito bom, so tive um problema usando o django_test_extensions:
    './manage.py runtester
    nosetests --verbosity 1 - - w i t h - n o t i f y
    Unhandled exception in thread started by '
    ....
    'ValueError: signal only works in main thread'
    Achei estranho que ele pos espaco entre cada letra do argumento do nosetests.
    Eu to usando virtualenv+wrapper, vc tem alguma nocao do que pode ser?
       Responder 
    Tem certeza que deseja  Sim  Não
    Insira sua mensagem aqui
  • muito bom =D
       Responder 
    Tem certeza que deseja  Sim  Não
    Insira sua mensagem aqui
  • Que bom que voces gostaram. Revampeei esse talk para a pythonbrasil de 2010 incluindo mais coisas como lettuce e afins..
       Responder 
    Tem certeza que deseja  Sim  Não
    Insira sua mensagem aqui
  • Concordo com o amigo Henrique, muito Irado!
       Responder 
    Tem certeza que deseja  Sim  Não
    Insira sua mensagem aqui
Sem downloads
Visualizações
Visualizações totais
5.392
No SlideShare
0
A partir de incorporações
0
Número de incorporações
250
Ações
Compartilhamentos
0
Downloads
153
Comentários
6
Gostaram
16
Incorporações 0
Nenhuma incorporação

Nenhuma nota no slide

Tdd em django sem desculpas versao final

  1. 1. #TDC2010
  2. 2. Código sem testes é código já quebrado quando foi planejado -- Jacob Kaplan-Moss um dos criadores do django
  3. 3. Estamos em 2010
  4. 4. 2010 > 1960
  5. 5. 2010 > 1999
  6. 6. Alguma buzzword "ágil" você tem que estar usando
  7. 7. Buzzwords são quintessenciais Buzzwords trazem sinergia viral e o empowerment das melhores práticas para a cauda longa
  8. 8. 3 anos atrás equipe: >40 pessoas numa mesma sala escopo: Webapp em Tomcat buzzwords: Bodyshop típico: cmms.. (< 1999)
  9. 9. 2.5 anos atrás equipe: 5 pessoas espalhadas pelo mundo escopo: Modificações no nível de uma distro buzzwords: Scrum, cultura de testes, sprints, entregas semanais
  10. 10. 5 > 40
  11. 11. Metodologias ágeis: Extreme Programing(XP) Scrum Kanban Feature Driven Develelopment (FDD)
  12. 12. Práticas ágeis: Test Driven Development (TDD) Behavior Driven Development (BDD) Code refactoring Continuous Integration Pair Programming Planning poker
  13. 13. TDD Sustentável Fácil Não depende da gerência
  14. 14. TDD não é díficil. Díficil é não fazer quando voce acostuma
  15. 15. Então, chega de desculpas:
  16. 16. Eu não sei nada sobre testes
  17. 17. O ecossistema de testes no python • Tipos • Sabores • TestRunners
  18. 18. Tipos de testes
  19. 19. Doctest def add(a,b): """ testa a soma >>> add(1,2) 3 """ return a + b
  20. 20. Unittest unittest.TestCase django.test.TestCase
  21. 21. django.test.TestCase from django.test import TestCase class SimpleTest(TestCase): def test_adicao(self): """ Testa que a adicao de 1 + 1 da 2. """ self.assertEqual(1 + 1, 2)
  22. 22. Sabores de testes
  23. 23. Unitários Nível de função self.assertTrue(add(1,2),3)
  24. 24. Integração Entre Módulos r = self.client.get('/foo') self.assertRedirects(r,'/login/') self.client.login(user_name='foo' ,password='bar') r = self.client.get('/foo') self.assertEquals(r.status_code,200)
  25. 25. De Regressão Correção de erros
  26. 26. TestRunners Acha e Roda os testes • Padrão • py.test • nose • outros
  27. 27. Meu estilo • Django.test.TestCase • Unitário Um TestCase por modelo Um ou mais testes por função
  28. 28. • Integração Um por TestCase por conjunto de apps • Regressão Um teste por erro • nose / django-nose Acha testes
  29. 29. Eu não preciso de testes automatizados
  30. 30. Código evolve
  31. 31. Se o seu código não tem testes refatorar ele é um pesadelo
  32. 32. Imagina isso $ cat `find . | grep "py$" | grep -v migration` | wc -l 47260
  33. 33. Agora isso: $ cat `find . | grep "py$" | grep test` | wc -l 34108
  34. 34. Tranquilidade de refatorar Felicidade é um código com boa cobertura
  35. 35. Eu meio que não sei o que é TDD
  36. 36. Ciência da computação é tanto sobre computadores quanto como a astronomia é sobre telescópios -- E W Dijkstra
  37. 37. Test Driven Development é tanto sobre testes assim quanto a ciência da computação é sobre computadores
  38. 38. TDD é sobre desenvolvimento e qualidade
  39. 39. Testes são um subproduto
  40. 40. TDD
  41. 41. TDD Só escreve código quando testes falham
  42. 42. TDD Só escreve código quando testes falham Só escreve testes quando testes passam
  43. 43. Eu nunca fiz muitos testes no Django
  44. 44. Como fazer: Instala o django $ easy_install pip $ pip install django
  45. 45. Cria o projeto $ django-admin.py startproject foobar $ cd foobar/ $ chmod +x manage.py $ vi settings.py
  46. 46. settings.py import os PROJECT_PATH = os.path.abspath( os.path.split(__file__)[0]) DATABASES = {'default': { 'ENGINE': 'django.db.backends.sqlite3', 'NAME': '/tmp/foobar.db', } } TEMPLATE_DIRS = ( os.path.join(PROJECT_PATH,'templates'), )
  47. 47. Hora de testar ./manage.py test ------------------------------------ Ran 0 tests in 0.000s OK Destroying test database 'default'...
  48. 48. TDD Só escreve código quando testes falham Só escreve testes quando testes passam
  49. 49. Passou Escreve testes
  50. 50. Mais Testes, então ./manage.py startapp forum cd forum/
  51. 51. Meu estilo (v.2) rm tests.py mkdir tests touch tests/__init__.py touch tests/test_models.py
  52. 52. vi tests/test_models.py #coding:utf8 from django.test import TestCase class ModelTest(TestCase):
  53. 53. Teste de importação def test_existe(self): """ O topico esta la? """ try: from foobar.forum.models import Topico except ImportError: self.fail('Nao existe topico')
  54. 54. Inclui a app no projeto INSTALLED_APPS = ( ... 'foobar.forum', )
  55. 55. Testa ./manage.py test ------------------------------------ Ran 0 tests in 0.000s OK Destroying test database 'default'...
  56. 56. 0 testes!
  57. 57. nose Acha testes para você sem que você tenha que por eles no __init__.py Dá pra chamar o pdb no ponto em que falha ( --pdb-failures) (ou ipdb)
  58. 58. django-nose $ pip install nose $ pip install django-nose $ pip install NoseNotify #opcional
  59. 59. settings.py TEST_RUNNER = 'django_nose.NoseTestSuiteRunner' INSTALLED_APPS = ( ... 'south', # migracoes 'django_nose', # depois do south ) NOSE_ARGS = [ '--with-notify', #'--pdb-failures', ]
  60. 60. Testa de novo F ==================================== FAIL: O topico esta la? ------------------------------------ Traceback (most recent call last): File "test_models", line 18, in test_existe self.fail('Nao existe topico') AssertionError: Nao existe topico ------------------------------------ Ran 1 test in 0.003s
  61. 61. TDD Só escreve código quando testes falham Só escreve testes quando testes passam
  62. 62. Falhou Escreve código
  63. 63. vi forum/models.py class Topico(models.Model): """representa um topico""" pass
  64. 64. testa . ------------------------------------ Ran 1 test in 0.014s
  65. 65. Pera! Voce gastou 8 slides para escrever um pass?
  66. 66. Mas TDD é muito lento
  67. 67. e por lento eu quero dizer chato
  68. 68. A primeira vez é lento
  69. 69. Entenda o que você esta testando try: from foobar.forum.models import Topico except ImportError: self.fail('Nao existe topico')
  70. 70. Não teste a framework Teste a lógica da sua applicação
  71. 71. Facilitadores
  72. 72. Continous testing Toda vez que você salva um arquivo ele rerola os testes
  73. 73. django test extensions Faz isso para você Ainda é um pouco tosco
  74. 74. django-test-extensions $ pip install django-test-extensions
  75. 75. settings.py INSTALLED_APPS = ( ... 'south', # migracoes 'django_nose', # depois do south 'test_extensions', # depois do south )
  76. 76. Rodando o servidor $ ./manage.py runtester ou ainda $ ./manage.py runtester forum
  77. 77. Automagicamente nosetests --verbosity 1 --with-notify forum ..... ------------------------------------------- Ran 5 tests in 0.457s OK Destroying test database 'default'... Creating test database 'default'...
  78. 78. Bonus combo django-test-extensions com NoseNotify
  79. 79. Mas eu não conheco todas as assertions
  80. 80. Bico
  81. 81. Modo mais fácil: no ./manage shell (com ipython instalado) >>> from django.test import TestCase >>> In [2]: TestCase.assert<tab><tab>
  82. 82. asserts TestCase.assert_ TestCase.assertAlmostEqual TestCase.assertAlmostEquals TestCase.assertContains TestCase.assertEqual TestCase.assertEquals TestCase.assertFalse TestCase.assertFormError TestCase.assertNotAlmostEquals TestCase.assertNotContains TestCase.assertNotEqual TestCase.assertNotEquals TestCase.assertRaises TestCase.assertRedirects TestCase.assertTemplateNotUsed TestCase.assertTemplateUsed TestCase.assertTrue TestCase.assertNotAlmostEqual
  83. 83. Asserts básicas Essas você deve usar bastante assertTrue(True) assertFalse(False) assertEqual(1,1) assertNotEqual(1,2)
  84. 84. Asserts amigáveis Essas facilitam a vida para testes funcionais assertContains(response,texto,status) assertNotContains(response,texto,status)
  85. 85. exemplo def test_welcome(self): resp = self.client.get('/',{}) self.assertContains(resp,'<h1>Oi</h1>' ,200)
  86. 86. Asserts amigáveis (cont) assertRedirects(response,nova_url) assertTemplateUsed(response,template) assertTemplateNotUsed(response,template) assertFormError(response,form,fields,errors)
  87. 87. WTF? assertAlmostEqual assertNotAlmostEqual
  88. 88. Não quase iguais? a = 1.21 b = 1.22 #sao iguais ate a primeira casa self.assertAlmostEqual(a,b,1) #diferentes depois da segunda casa self.assertNotAlmostEqual(a,b,2)
  89. 89. Asserts que eu não uso assertRaises
  90. 90. Testo assim: try: foobar.bang(): self.fail('Bang tem que explodir') except ExplodingException: pass
  91. 91. Agora é tarde demais para TDD, meu projeto já existe
  92. 92. Pera! Olha só • Testes de Regressão • django_test_utils
  93. 93. Seu melhor amigo Garante que um erro que aconteceu nunca mais volte a acontecer Usado por todos os grandes projetos de software livre Mesmo você não vai fazer mais nenhuma forma de teste você tem que fazer esta
  94. 94. Testes de Regressão
  95. 95. Encontrou um erro [24/Jul/2010 11:14:51] "GET / HTTP/1.1" 404 1946
  96. 96. Escreve um teste que falha por causa do erro $ vi forum/test_regression.py
  97. 97. cont #coding:utf8 from django.test import TestCase class TestRegression(TestCase): """testes de regressao"""
  98. 98. cont+=1 def test_regress_home(self): """Home precisa existir""" r = self.client.get('/', {}) self.assertEqual(r.status_code, 200)
  99. 99. Testa e falha ..E ================================================ ERROR: Home precisa existir ------------------------------------------------ Traceback (most recent call last): File "foobar/forum/tests/test_regresssion.py", line 10, in test_regress_home r = self.client.get('/', {}) ... raise TemplateDoesNotExist(name) TemplateDoesNotExist: 404.html
  100. 100. Corrige o erro from django.views.generic.simple import direct_to_template urlpatterns = patterns('', ... (r'^$', direct_to_template, {'template': 'index.html'}), ... ) $ vi templates/index.html
  101. 101. Roda os testes e passa nosetests --verbosity 1 .... ----------------------- Ran 4 tests in 0.025s OK
  102. 102. Garantia que erros antigos não vão retornar para te assombrar
  103. 103. Toda vez que eu começo com TDD mas acabo desistindo no meio
  104. 104. 2 formas sustentáveis para começar e continuar com TDD
  105. 105. Primeiro:
  106. 106. TDD:Eu queria ter isso Você escreve nos testes a API que você queria ter
  107. 107. Eu queria que fosse assim: def test_metodos(self): topico = Topico() self.assertTrue(hasattr(topico, 'titulo')) self.assertTrue(hasattr(topico, 'replies'))
  108. 108. Testa F. ================================================= FAIL: test_metodos (test_forum.TestForum) ------------------------------------------------- Traceback (most recent call last): self.assertTrue(hasattr(topico, 'titulo')) AssertionError -------------------------------------------------- Ran 2 tests in 0.002s FAILED (failures=1)
  109. 109. Implementa class Topico(models.Model): """representa um topico""" titulo = models.CharField(max_length=64) class Resposta(models.Model): '''Uma resposta no topico''' topico = models.ForeignKey(Topico, related_name='replies')
  110. 110. Testa .. -------------------------------------------------- Ran 2 tests in 0.002s OK
  111. 111. Prós e Cons • Não é exatamente TDD • Funciona • Mais rápido • Você está perdendo cobertura
  112. 112. Segundo: SDT
  113. 113. SDT Eu não faço TDD eu faco Stupidity-driven testing. Quando eu faco algo estúpido, eu escrevo um teste para garantir que eu não vou repetir isso de novo --Titus Brown pycon '07
  114. 114. Em suma Escreve código para solucinar um problema Se o código quebrar de alguma forma besta Escreve um teste para isso nunca vai acontecer de novo goto 10
  115. 115. Prós e Cons • Não é TDD • Funciona mas beira Cowboyismo • Cobertura só sobre o código mais frágil • Lembra teste de regressão
  116. 116. Por que lembra um teste de regressão? Porque é. São testes de regressão para você mesmo.
  117. 117. Escrever testes é mais complicado que o problema
  118. 118. Longo sim, complicado não Especialmente longo para testes funcionais django_test_utils, o utlimo bastião dos preguiçosos
  119. 119. django-test-utils $ pip install django-test-utils
  120. 120. settings.py INSTALLED_APPS = ( ... 'south', # migracoes 'django_nose', # depois do south 'test_extensions', # depois do south 'test_utils', # depois do south ... )
  121. 121. Você começa o servidor $ ./manage.py testmaker -a forum
  122. 122. Cria testes para você Handling app 'forum' Logging tests to foobar/forum/tests/forum_testmaker.py Appending to current log file Inserting TestMaker logging server... Validating models... 0 errors found Django version 1.2.1, using settings 'foobar.settings' Development server is running at http://127.0.0.1:8000/ Quit the server with CONTROL-C.
  123. 123. Quando você termina $ cd forum/tests $ ls forum* forum_testdata.serialized forum_testmaker.py
  124. 124. Testes gerados def test_forum_127958317459(self): r = self.client.get('/forum/', {}) self.assertEqual(r.status_code, 200) self.assertEqual( unicode(r.context["paginator"]), u"None") self.assertEqual( unicode(r.context["object_list"]), u"[<Topico: Topico object>, <Topico: Topico object>]") .....
  125. 125. Não, não! Escrever testes é mais complicado que o problema, mesmo!
  126. 126. Mocks e Stubs
  127. 127. Minta descaradamente para seu código
  128. 128. Imagina algo assim: def calcula_queijo(request): sanduba = request.session["sanduba"] ....
  129. 129. Não perca tempo Colocar algo no session do request em um test case é chato
  130. 130. Hora de mockear Existem muitas ferramentas de mock para python Escolha uma e vai fundo. Não esqueça de RTFM Eu uso o fudge
  131. 131. Instalar e usar pip install fudge
  132. 132. Seu teste import fudge ... def teste(self) request = fudge.Fake().has_attr( session={'sanduba': Sanduba() }) calcula_queijo(request)
  133. 133. Eu conserto os testes depois
  134. 134. PFFFFFFFFFF!
  135. 135. TDD não é díficil. Díficil é não fazer quando voce acostuma
  136. 136. Créditos http://www.flickr.com/photos/blue-moose/3528603529
  137. 137. Dúvidas?
  138. 138. Agradecimentos http://associacao.python.org.br/ Nos vemos na PythonBrasil[6] em Curitiba Outubro 21 a 23
  139. 139. Referências http://code.google.com/p/python-nose/ http://github.com/jbalogh/django-nose http://github.com/garethr/django-test-extensions http://github.com/ericholscher/django-test-utils http://github.com/ctb/pony-build Tdd em django sem desculpas @fractal petrich@gmail.com creative commons (by) (sa)

×