Se ha denunciado esta presentación.
Se está descargando tu SlideShare. ×

Pruebas unitarias con django

Anuncio
Anuncio
Anuncio
Anuncio
Anuncio
Anuncio
Anuncio
Anuncio
Anuncio
Anuncio
Anuncio
Anuncio
Próximo SlideShare
Diagramas de estado
Diagramas de estado
Cargando en…3
×

Eche un vistazo a continuación

1 de 67 Anuncio

Más Contenido Relacionado

Presentaciones para usted (20)

Similares a Pruebas unitarias con django (20)

Anuncio

Más reciente (20)

Pruebas unitarias con django

  1. 1. Pruebas Unitarias con Django Tomás Henríquez tchenriquez@gmail.com 2-11-2012
  2. 2. Historia de las pruebas en el software http://clearspecs.com/joomla15/downloads/ClearSpecs16V01_GrowthOfSoftwareTest.pdf
  3. 3. Historia de las pruebas Período orientado al Debugging (antes - 1956)
  4. 4. Historia de las pruebas Período orientado a la demostación (1957 - 1978)
  5. 5. Historia de las pruebas
  6. 6. Historia de las pruebas Período orientado a la destrucción (1979 - 1982)
  7. 7. Historia de las pruebas Período orientado a la evaluación (1983 - 1987)
  8. 8. Historia de las pruebas “Ninguna técnica puede garantizar software sin errores, sin embargo, un conjunto de técnicas cuidadosamente elegidas para un proyecto en específico puede ayudar a asegurar el desarrollo y mantenimiento de software de calidad para un proyecto”
  9. 9. Historia de las pruebas Período orientado a la prevención (1988 - Hoy)
  10. 10. Pruebas Unitarias
  11. 11. Pruebas ¿Cómo uno usualmente prueba su código?
  12. 12. Pruebas ¿Qué problemas tiene el debugging?
  13. 13. Pruebas ¿Cómo persistir estas pruebas?
  14. 14. Pruebas ¡NO ES FÁCIL!
  15. 15. Pruebas ¡NO ES FÁCIL! ● Carga de datos para la prueba ● No debe afectar la base de datos ● Super rápidas
  16. 16. Sheet Got Serious
  17. 17. Carga de datos FIXTURES
  18. 18. Carga de datos class Profession(models.Model): name = models.CharField(max_length=30, unique=True) specialist = models.CharField(max_length=30, blank=True)
  19. 19. Carga de datos [ { "pk": 1, "model": "my_app.profession", "fields": { "name": "panadero", "specialist": "pizza", } }, { "pk": 2, "model": "my_app.profession", "fields": { "name": "forever_alone", ... ]
  20. 20. Carga de datos class Profession(models.Model): name = models.CharField(max_length=30, unique=True) specialist = models.CharField(max_length=30, blank=True) full_time = models.BooleanField(default=False) <----
  21. 21. Carga de datos [ { "pk": 1, "model": "my_app.profession", "fields": { "name": "panadero", "specialist": "pizza", "full_time": false, <--- Ay no..... } }, { "pk": 2, "model": "my_app.profession", "fields": { "name": "forever_alone", ... ]
  22. 22. Carga de datos [ { "pk": 1, "model": "my_app.profession", "fields": { "name": "panadero", "specialist": "pizza", "full_time": false, <--- Ay no..... } }, { "pk": 2, "model": "my_app.profession", "fields": { "name": "forever_alone", ... ]
  23. 23. Carga de datos FIXTURES ¿Para qué son buenos?
  24. 24. Carga de datos Estructura del App my_app/ __init__.py models.py fixtures/base.json <------ tests.py
  25. 25. Carga de datos class UserTest(TestCase): fixtures = ['base.json'] def setUp(self): pass def tearDown(self): pass def test_coolness(self): # usuario con id == 1 existe gracias al fixture user = User.objects.get(id=1) self.assertTrue(user.is_cool, False) user.become_a_hipster() self.assertTrue(user.is_cool, True)
  26. 26. Carga de datos Factories https://github.com/dnerdy/factory_boy
  27. 27. Carga de datos Factories ● Organizado <------
  28. 28. Carga de datos from django.db import models class Receipt(models.Model): user_id = fields.IntegerField() merchant = fields.CharField(max_length=30) class ReceiptItem(models.Model): name = models.CharField(max_length=30) quantity = models.IntegerField(default=1) alert_sent = models.BooleanField(default=False) receipt = models.ForeignKey(Receipt) class Attach(models.Model): name = models.CharField(max_length=30) item = models.ForeignKey(ReceiptItem)
  29. 29. Carga de datos class ReceiptTest(TestCase): fixtures = ['base.json'] def setUp(self): rep = Receipt(user_id=1, merchant='mami') rep.save() ri = ReceiptItem(name='medias', quantity=2, receipt=rep) ri.save() att = Attach(name='foto_de_juanita.jpg', item=ri) att.save() ...
  30. 30. Carga de datos class ReceiptTest(TestCase): fixtures = ['base.json'] def setUp(self): rep = Receipt(user_id=1, merchant='mami') rep.save() ri = ReceiptItem(name='medias', quantity=2) ri.save() att = Attach(name='foto_de_juanita.jpg', item=ri) att.save() ...
  31. 31. Carga de datos Estructura del App my_app/ __init__.py models.py fixtures/base.json factories.py <------- tests.py
  32. 32. Carga de datos class ReceiptFactory(factory.Factory): FACTORY_FOR = Receipt user_id = 1 merchant = "mami" class ReceiptItemFactory(factory.Factory): FACTORY_FOR = ReceiptItem name = factory.Sequence(lambda n: 'item-%s' % n) quantity = 1 receipt = ReceiptFactory() class AttachmentFactory(factory.Factory): FACTORY_FOR = Attachment name = factory.Sequence(lambda n: 'attachment-%s' % n) item = ReceiptItemFactory()
  33. 33. Carga de datos class ReceiptTest(TestCase): fixtures = ['base.json'] def setUp(self): ReceiptFactory() ...
  34. 34. Carga de datos
  35. 35. Carga de datos
  36. 36. Carga de datos
  37. 37. Carga de datos Factories ● Organizado ● Flexible <-------
  38. 38. Carga de datos class ReceiptTest(TestCase): fixtures = ['base.json'] def setUp(self): # No se crea en base de datos ReceiptFactory.build() ...
  39. 39. Carga de datos class ReceiptTest(TestCase): fixtures = ['base.json'] def setUp(self): # Crear muchos objects ReceiptFactory.create_batch(5) ...
  40. 40. Carga de datos Factories ● Organizado ● Flexible ● Facil de migrar <-------
  41. 41. Carga de datos from mongoengine.document import Document, EmbeddedDocument class Attach(EmbeddedDocument): name = fields.StringField(required=True) class ReceiptItem(EmbeddedDocument): name = fields.StringField(required=True) quantity = fields.DecimalField(default=Decimal(1)) alert_sent = fields.BooleanField(default=False) attachments = fields.ListField(fields.EmbeddedDocumentField(Attach)) class Receipt(Document): user_id = fields.IntField(required=True) merchant = fields.StringField(required=True) items = fields.ListField(fields.EmbeddedDocumentField(ReceiptItem))
  42. 42. Carga de datos class AttachmentFactory(factory.Factory): FACTORY_FOR = Attachment name = factory.Sequence(lambda n: 'attachment-%s' % n) class ReceiptItemFactory(factory.Factory): FACTORY_FOR = ReceiptItem name = factory.Sequence(lambda n: 'item-%s' % n) quantity = 1 Attachments = [AttachmentFactory.build()] class ReceiptFactory(factory.Factory): FACTORY_FOR = Receipt user_id = 2 merchant = "Amazon" items = [ReceiptItemFactory.build()]
  43. 43. Carga de datos class ReceiptTest(TestCase): fixtures = ['base.json'] def setUp(self): ReceiptFactory() ...
  44. 44. MOCKERS http://labix.org/mocker http://www.voidspace.org.uk/python/mock/
  45. 45. Mockers
  46. 46. Mockers def tweet(tokens, body): consumer = oauth.Consumer(TWITTER_KEY, settings.TWITTER_SECRET) token = oauth.Token(tokens.oauth, tokens.oauth_secret) client = oauth.Client(consumer, token) header, body = client.request("http://api.twitter.com/1/statuses/" "update.json", "POST", body="status=%s" % body) if header['status'] == '401': return False, ('Twitter account not authorized.' ' Please connect your account again.') body = json.loads(body) if header['status'] != '200': return False, body.get('error', 'Unknown Twitter error') return True, sbody['id_str']
  47. 47. Mockers from mocker import Mocker, ARGS, KWARGS class TweetTest(TestCase): fixtures = ['base.json'] def setUp(self): mocker = Mocker() mock_client = mocker.replace('oauth.Client') mock_client.request(ARGS, KWARGS) mocker.response({'code': 200, 'text': 'OK', 'description': 'Success!'}) <---- self.mocker = mocker def test_tweet(self): user = User.objects.get(id=1) self.assertTrue(user.tweets, 0) with self.mocker: res = tweet(user.tokens, 'esto es un Tweet de prueba') self.assertEquals(res, True)
  48. 48. Mockers from mocker import Mocker, ARGS, KWARGS class TweetTest(TestCase): fixtures = ['base.json'] def setUp(self): mocker = Mocker() mock_client = mocker.replace('oauth.Client') mock_client.request(ARGS, KWARGS) mocker.throw(ConnectionException) <---- self.mocker = mocker def test_tweet(self): user = User.objects.get(id=1) # Podemos manejar la excepcion? with self.mocker: tweet(user.tokens, 'esto es un Tweet de prueba')
  49. 49. Buenas y Malas Practicas
  50. 50. Tips ¿Hacer Pruebas antes de lanzar código?
  51. 51. Tips MAL def test_gmail(self): expected_msg = { 'subject': 'SeamlessWeb Order', 'plain': '[image: Seamless Web Logo] ...' # MAL 'html': 'n<table width="640" border=...' # MAL } m = Message.objects.get(pk=1) msg = { 'subject': m.subject, 'html': m.body_mimetype_html, 'plain': m.body_mimetype_plain } msg = strip_forwarding(msg) self.assertEqual(msg['subject'], expected_msg['subject']) self.assertEqual(msg['plain'], expected_msg['plain']) self.assertEqual(msg['html'], expected_msg['html'])
  52. 52. Tips BIEN def test_gmail(self): strips = ('---------- Forwarded message ----------', 'Fwd: ', 'From: ', 'Date: ', 'Subject: ', 'To: ') checks = {'subject': u'SeamlessWeb Order'} msg = strip_forwarding(msg) # Validar que los valores sean los esperados for key, val in checks.items(): self.assertEqual(val, msg[key]) # Asegurar que estos valores no se encuentren en el email for strip in strips: for m in msg.values(): self.assertNotIn(strip, m)
  53. 53. Tips No hacer multiples pruebas en una función
  54. 54. Tips MAL def test_emails(self): strips = ('---------- Forwarded message ----------', 'Fwd: ', 'From: ', 'Date: ', 'Subject: ', 'To: ') checks = {'subject': u'SeamlessWeb Order'} gmail, aol, hotmail = message.objects.all()[:2] msgs = (strip_forwarding(gmail), strip_forwarding(aol), strip_forwarding(hotmail)) for msg in msgs: for key, val in checks.items(): self.assertEqual(val, msg[key]) for strip in strips: for m in msg.values(): self.assertNotIn(strip, m)
  55. 55. Tips BIEN def test_gmail(self): strips = ('---------- Forwarded message ----------', 'Fwd: ', 'From: ', 'Date: ', 'Subject: ', 'To: ') checks = {'subject': u'SeamlessWeb Order'} gmail = message.objects.get(id=1) msgs = strip_forwarding(gmail) for key, val in checks.items(): self.assertEqual(val, msg[key]) for strip in strips: for m in msg.values(): self.assertNotIn(strip, m) def test_aol(self): strips = ('---------- Forwarded message ----------',...
  56. 56. Tips BIEN def test_gmail(self): strips = ('---------- Forwarded message ----------', 'Fwd: ', 'From: ', 'Date: ', 'Subject: ', 'To: ') checks = {'subject': u'SeamlessWeb Order'} gmail = message.objects.get(id=1) msgs = strip_forwarding(gmail) for key, val in checks.items(): self.assertEqual(val, msg[key]) for strip in strips: for m in msg.values(): self.assertNotIn(strip, m) def test_aol(self): strips = ('---------- Forwarded message ----------',...
  57. 57. Tips Pruebas por Aserción def test_gmail_checks(self): checks = {'subject': u'SeamlessWeb Order'} gmail = message.objects.get(id=1) msgs = strip_forwarding(gmail) for key, val in checks.items(): self.assertEqual(val, msg[key]) <---- Asercion def test_gmail_strips(self): strips = ('---------- Forwarded message ----------', 'Fwd: ', 'From: ', 'Date: ', 'Subject: ', 'To: ') gmail = message.objects.get(id=1) msgs = strip_forwarding(gmail) for strip in strips: for m in msg.values(): self.assertNotIn(strip, m) <---- Asercion
  58. 58. Tips Pruebas por Acción def test_gmail(self): strips = ('---------- Forwarded message ----------', 'Fwd: ', 'From: ', 'Date: ', 'Subject: ', 'To: ') checks = {'subject': u'SeamlessWeb Order'} gmail = message.objects.get(id=1) msgs = strip_forwarding(gmail) <---- ACCION for key, val in checks.items(): self.assertEqual(val, msg[key]) for strip in strips: for m in msg.values(): self.assertNotIn(strip, m)
  59. 59. Tips Probar todas las ramas def handle_coolness(self, user): if user.is_cool: do_cool_stuff(user) else: raise NotCoolDudeException
  60. 60. Pruebas de Integración
  61. 61. Pruebas de Integración django client library def test_view(self): user = User.objects.get(id=1) url = '/login/' response = self.client.post(url, { 'username': user.username, 'password': 'fakybaby', 'timezone': 'America/Caracas' } self.assertEquals(response.status_code, 200) self.assertEquals(response.cookies_set['logged_name'], User.username)
  62. 62. Pruebas de Integración django client library def test_view(self): user = User.objects.get(id=1) url = '/login/' response = self.client.post(url, { 'username': user.username, 'password': 'fakybaby', 'timezone': 'America/Caracas' } self.assertEquals(response.status_code, 200) self.assertEquals(response.cookies_set['logged_name'], User.username)
  63. 63. Pruebas de Integración WebTest! def test_view(self): user = User.objects.get(id=1) url = '/login/' form = self.app.get(url).forms['login-form'] form['username'] = user.username form['password'] = 'fakybaby' response = form.submit().follow() self.assertEquals(response.status_code, 200) self.assertEquals(response.cookies_set['logged_name'], user.username)
  64. 64. Continuous deployment http://jenkins-ci.org/
  65. 65. Continuous Deployment Integrado por django_jenkins Correr Pruebas unitarias Verificacion de codigo (Pep8, jslint, etc) Reportes Emails
  66. 66. Continuous Deployment if len(sys.argv) > 1 and sys.argv[1] in ['test', 'jenkins']: # test mongo db MONGO_DBNAME = 'db_test' db = mongoengine.connect(MONGO_DBNAME, username=MONGO_USERNAME, password=MONGO_PASSWORD, host=MONGO_HOST, port=MONGO_PORT, safe=True) db.drop_database(MONGO_DBNAME) # drop entire database
  67. 67. ¿Preguntas? @hazuek tchenriquez@gmail.com github.com/hassek

Notas del editor

  • Make sure it runs Make sure it solves the problem
  • Una ves las aprendes a hacer, siempre es mas rapido hace Pruebas que persistan
  • Buscar foto que me ayude con el texto Crear info inicial Que no afecte tu base de datos Super Fast!
  • Buscar foto que me ayude con el texto Crear info inicial Que no afecte tu base de datos Super Fast!
  • Colorizar este codigo
  • Colorizar este codigo
  • Colorizar este codigo
  • Colorizar este codigo
  • Colorizar este codigo

×