Prueba Sun It Arias Con Django

67
Pruebas Unitarias con Django Tomás Henríquez [email protected] 2-11-2012

description

Tomás Henrí[email protected]

Transcript of Prueba Sun It Arias Con Django

Page 1: Prueba Sun It Arias Con Django

Pruebas Unitarias con Django Tomás Henríquez [email protected] 2-11-2012

Page 2: Prueba Sun It Arias Con Django

Historia de las pruebas

en el software

http://clearspecs.com/joomla15/downloads/ClearSpecs16V01_GrowthOfSoftwareTest.pdf

Page 3: Prueba Sun It Arias Con Django

Historia de las pruebas

Período orientado al Debugging

(antes - 1956)

Page 4: Prueba Sun It Arias Con Django

Historia de las pruebas

Período orientado a la demostación

(1957 - 1978)

Page 5: Prueba Sun It Arias Con Django

Historia de las pruebas

Page 6: Prueba Sun It Arias Con Django

Historia de las pruebas

Período orientado a la destrucción

(1979 - 1982)

Page 7: Prueba Sun It Arias Con Django

Historia de las pruebas

Período orientado a la evaluación

(1983 - 1987)

Page 8: Prueba Sun It Arias Con Django

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”

Page 9: Prueba Sun It Arias Con Django

Historia de las pruebas

Período orientado a la prevención

(1988 - Hoy)

Page 10: Prueba Sun It Arias Con Django

Pruebas Unitarias

Page 11: Prueba Sun It Arias Con Django

Pruebas

¿Cómo uno usualmente prueba

su código?

Page 12: Prueba Sun It Arias Con Django

Pruebas

¿Qué problemas tiene

el debugging?

Page 13: Prueba Sun It Arias Con Django

Pruebas

¿Cómo persistir

estas pruebas?

Page 14: Prueba Sun It Arias Con Django

Pruebas

¡NO ES FÁCIL!

Page 15: Prueba Sun It Arias Con Django

Pruebas

¡NO ES FÁCIL!

● Carga de datos para la prueba● No debe afectar la base de datos● Super rápidas

Page 16: Prueba Sun It Arias Con Django

Sheet Got Serious

Page 17: Prueba Sun It Arias Con Django

Carga de datos

FIXTURES

Page 18: Prueba Sun It Arias Con Django

Carga de datos

class Profession(models.Model): name = models.CharField(max_length=30, unique=True) specialist = models.CharField(max_length=30, blank=True)

Page 19: Prueba Sun It Arias Con Django

Carga de datos[ { "pk": 1, "model": "my_app.profession", "fields": { "name": "panadero", "specialist": "pizza", } }, { "pk": 2, "model": "my_app.profession", "fields": { "name": "forever_alone", ... ]

Page 20: Prueba Sun It Arias Con Django

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

Page 21: Prueba Sun It Arias Con Django

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", ... ]

Page 22: Prueba Sun It Arias Con Django

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", ... ]

Page 23: Prueba Sun It Arias Con Django

Carga de datos

FIXTURES

¿Para qué son buenos?

Page 24: Prueba Sun It Arias Con Django

Carga de datos

Estructura del App

my_app/

__init__.py

models.py

fixtures/base.json <------

tests.py

Page 25: Prueba Sun It Arias Con Django

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)

Page 26: Prueba Sun It Arias Con Django

Carga de datos

Factories

https://github.com/dnerdy/factory_boy

Page 27: Prueba Sun It Arias Con Django

Carga de datos

Factories● Organizado <------

Page 28: Prueba Sun It Arias Con Django

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)

Page 29: Prueba Sun It Arias Con Django

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

Page 30: Prueba Sun It Arias Con Django

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

Page 31: Prueba Sun It Arias Con Django

Carga de datos

Estructura del App

my_app/

__init__.py

models.py

fixtures/base.json

factories.py <-------

tests.py

Page 32: Prueba Sun It Arias Con Django

Carga de datosclass 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()

Page 33: Prueba Sun It Arias Con Django

Carga de datosclass ReceiptTest(TestCase): fixtures = ['base.json']

def setUp(self): ReceiptFactory() ...

Page 34: Prueba Sun It Arias Con Django

Carga de datos

Page 35: Prueba Sun It Arias Con Django

Carga de datos

Page 36: Prueba Sun It Arias Con Django

Carga de datos

Page 37: Prueba Sun It Arias Con Django

Carga de datos

Factories● Organizado● Flexible <-------

Page 38: Prueba Sun It Arias Con Django

Carga de datosclass ReceiptTest(TestCase): fixtures = ['base.json']

def setUp(self): # No se crea en base de datos ReceiptFactory.build() ...

Page 39: Prueba Sun It Arias Con Django

Carga de datosclass ReceiptTest(TestCase): fixtures = ['base.json']

def setUp(self): # Crear muchos objects ReceiptFactory.create_batch(5) ...

Page 40: Prueba Sun It Arias Con Django

Carga de datos

Factories● Organizado● Flexible● Facil de migrar <-------

Page 41: Prueba Sun It Arias Con Django

Carga de datosfrom 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))

Page 42: Prueba Sun It Arias Con Django

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

Page 43: Prueba Sun It Arias Con Django

Carga de datosclass ReceiptTest(TestCase): fixtures = ['base.json']

def setUp(self): ReceiptFactory() ...

Page 44: Prueba Sun It Arias Con Django

MOCKERS

http://labix.org/mocker

http://www.voidspace.org.uk/python/mock/

Page 45: Prueba Sun It Arias Con Django

Mockers

Page 46: Prueba Sun It Arias Con Django

Mockersdef 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']

Page 47: Prueba Sun It Arias Con Django

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

Page 48: Prueba Sun It Arias Con Django

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

Page 49: Prueba Sun It Arias Con Django

Buenas y MalasPracticas

Page 50: Prueba Sun It Arias Con Django

Tips

¿Hacer Pruebas antes de lanzar código?

Page 51: Prueba Sun It Arias Con Django

TipsMALMAL

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

Page 52: Prueba Sun It Arias Con Django

TipsBIENBIEN

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)

Page 53: Prueba Sun It Arias Con Django

Tips

No hacer multiples pruebas en una función

Page 54: Prueba Sun It Arias Con Django

TipsMALMAL

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)

Page 55: Prueba Sun It Arias Con Django

TipsBIENBIEN

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 ----------',...

Page 56: Prueba Sun It Arias Con Django

TipsBIENBIEN

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 ----------',...

Page 57: Prueba Sun It Arias Con Django

TipsPruebas por AserciónPruebas 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

Page 58: Prueba Sun It Arias Con Django

TipsPruebas por AcciónPruebas 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)

Page 59: Prueba Sun It Arias Con Django

TipsProbar todas las ramasProbar todas las ramas

def handle_coolness(self, user): if user.is_cool: do_cool_stuff(user) else: raise NotCoolDudeException

Page 60: Prueba Sun It Arias Con Django

Pruebas de Integración

Page 61: Prueba Sun It Arias Con Django

Pruebas de Integracióndjango client librarydjango 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)

Page 62: Prueba Sun It Arias Con Django

Pruebas de Integracióndjango client librarydjango 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)

Page 63: Prueba Sun It Arias Con Django

Pruebas de IntegraciónWebTest!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)

Page 64: Prueba Sun It Arias Con Django

Continuous deployment

http://jenkins-ci.org/

Page 65: Prueba Sun It Arias Con Django

Continuous Deployment

Integrado por django_jenkins

Correr Pruebas unitarias

Verificacion de codigo (Pep8, jslint, etc)

Reportes

Emails

Page 66: Prueba Sun It Arias Con Django

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

Page 67: Prueba Sun It Arias Con Django

¿Preguntas?

@hazuek

[email protected]

github.com/hassek