DJANGO,
PYTEST, TOX, Y.. ACCIÓN!
Angel Velásquez <angvp@archlinux.org>
social stuff: @angvp
Para los que no me conocen...

- Usando Django desde el 2010~
- Programando desde antes de eso ^
- Arch Linux Developer
- Coach del taller de Django Girls organizado por LinuxChix y Argentina en Python en la PyCon Argentina Mendoza 2015.
- Contribuyo en varias apps para django: django-changuito, django-klingon, django-oml, drf-lafv, rpc4django, etc..
Algunas preguntas
- Quién debería escribir tests?
- Por qué deberían escribirse tests?
- Cuáles son los mitos mas repetidos del testing?
¿Qué puedo testear de mi proyecto?
- Modelos
- Vistas
- Formularios
- Widgets de algunos forms
- Comandos
- template tags
- ... etc
¿Por qué pytest?
- Código mas python
- Atracciones innovadoras: (sistema de plugins, fixtures)
- Plugins que te van a ayudar a escribir mejores programas en una sola herramienta!: (django, cov, sugar, xdist, pep8, etc)
- Juega bien con otros (nose, unittest)
- El parámetro --pdb ! si un test se rompe podemos entrar a debuggear de una vez, super útil para los amantes del TDD
Testeando un modelo
from __future__ import unicode_literals
from django.db import models
class Book(models.Model):
title = models.CharField(max_length=200)
isbn = models.CharField(max_length=20)
value = models.DecimalField(max_digits=5, decimal_places=2)
def __unicode__(self):
return "The book {} costs: {}".format(self.title, self.value)La idea no es probar campo por campo, sabemos que Django tiene una batería de tests donde se probó todo esto, la idea acá sería hacer tests las cosas nuestras, en este caso vamos a hacer test de__unicode__
Testeando un modelo (unittest)
from ..models import Book
from django.test import TestCase
from decimal import Decimal
# una clase 2 métodos + lowerCamelCase
class BookModelTestCase(TestCase):
def setUp(self):
book_data = {
'title': 'meetup 12 2015',
'isbn': '1112223334445',
'value': Decimal('132.23')
}
self.book = Book.objects.create(**book_data)
def test_unicode(self):
self.assertEquals(self.book.__unicode__(),
"The book {} costs: {}".format(
"meetup 12 2015", Decimal('132.23')))
$ python manage.py test
Creating test database for alias 'default'...
.
----------------------------------------------------------------------
Ran 1 test in 0.001s
OK
Destroying test database for alias 'default'...Testeando un modelo (pytest)
import pytest
from ..models import Book
from decimal import Decimal
pytestmark = pytest.mark.django_db
@pytest.fixture()
def create_book():
book_data = {
'title': 'meetup 12 2015',
'isbn': '1112223334445',
'value': Decimal('132.23')
}
return Book.objects.create(**book_data)
def test_unicode(create_book):
book = create_book
assert book.__unicode__() == "The book {} costs: {}".format(
"meetup 12 2015", Decimal('132.23'))py.test -q
.
1 passed in 0.27 seconds
Fixtures de pytest-django
from django.core.urlresolvers import reverse
def test_change_settings(settings):
# despues de que este test corre se resetea el settings de nuevo
settings.DATE_FORMAT = 'd-m-Y'
def test_index(client):
# client es una instancia de django.test.TestCase.TestClient
url = reverse('index')
rq = client.get(url)
assert rq.status_code == 200Algunos plugins (cov)
$ py.test --cov book
================================== test session starts ==================================
platform linux2 -- Python 2.7.11, pytest-2.8.5, py-1.4.31, pluggy-0.3.1
django settings: dpte.settings (from ini file)
rootdir: /home/angvp/workspace/git/dpte, inifile: pytest.ini
plugins: pep8-1.0.6, cov-2.2.0, django-2.9.1
collected 1 items
book/tests/test_models.py .
------------------- coverage: platform linux2, python 2.7.11-final-0 --------------------
Name Stmts Miss Cover
-----------------------------------------------------
book/__init__.py 0 0 100%
book/admin.py 1 0 100%
book/apps.py 3 3 0%
book/migrations/0001_initial.py 6 0 100%
book/migrations/__init__.py 0 0 100%
book/models.py 8 0 100%
book/tests/__init__.py 0 0 100%
book/tests/test_models.py 10 0 100%
book/views.py 1 1 0%
-----------------------------------------------------
TOTAL 29 4 86%
=============================== 1 passed in 0.33 seconds ================================$ pip install pytest-cov Algunos plugins (sugar)
$ py.test book
Test session starts (platform: linux2, Python 2.7.11, pytest 2.8.5, pytest-sugar 0.5.1)
django settings: dpte.settings (from ini file)
rootdir: /home/angvp/workspace/git/dpte, inifile: pytest.ini
plugins: pep8-1.0.6, cov-2.2.0, django-2.9.1, sugar-0.5.1
book/tests/test_models.py ✓ 100% ██████████
Results (0.26s):
1 passed
$ pip install pytest-sugarNota: sugar puede romper compatibilidad con otros plugins, pero para tener colores y ver algunas gráficas de progreso en la terminal de forma "linda" está genial. En este caso la slide no muestra, pero les juro que tiene colores!
Algunos plugins (xdist)
$ py.test -n 4
================================= test session starts =================================
platform linux2 -- Python 2.7.11, pytest-2.8.5, py-1.4.31, pluggy-0.3.1
django settings: dpte.settings (from ini file)
rootdir: /home/angvp/workspace/git/dpte, inifile: pytest.ini
plugins: pep8-1.0.6, xdist-1.13.1, cov-2.2.0, django-2.9.1
gw0 [1] / gw1 [1] / gw2 [1] / gw3 [1]
scheduling tests via LoadScheduling
.
============================== 1 passed in 1.26 seconds ===============================$ pip install pytest-xdistNota: No tiene sentido correr xdist con 1 solo test ;). Django soporta tests en paralelo desde la versión 1.9
Testeando en varios entornos
[tox]
skipsdist=True # para testear un proyecto y no un build
envlist =
{py27,py34}-django{1.8,1.9}
{py35}-django{1.8,1.9}
[testenv]
commands = py.test -q
deps =
django1.7: Django<1.8
django1.8: Django<1.9
django1.9: Django<1.10
pytest-django==2.9.1tox es una herramienta que permite automatizar la creación de entornos virtuales y correr los tests que tenemos en diferentes versiones de Django, python se debe definir un archivo tox.ini en la raiz del proyecto
$ tox # and the magic begins ;)Mas de tox
$ tox -e py27-django1.8,py27-django1.9 # tests de solo python27 y django 1.8 + 1.9
py27-django1.8 installed: Django==1.8.7,py==1.4.31,pytest==2.8.5,pytest-django==2.9.1,wheel==0.24.0
py27-django1.8 runtests: PYTHONHASHSEED='562148675'
py27-django1.8 runtests: commands[0] | py.test -qq
...
py27-django1.9 installed: Django==1.9,py==1.4.31,pytest==2.8.5,pytest-django==2.9.1,wheel==0.24.0
py27-django1.9 runtests: PYTHONHASHSEED='562148675'
py27-django1.9 runtests: commands[0] | py.test -qq
...
_______________________________________________ summary ________________________________________________
py27-django1.8: commands succeeded
py27-django1.9: commands succeeded
congratulations :)
Algunas otras opciones de tox son:
- -e : Para pasar la lista de entornos definidos en tox.ini
- -l : Lista de los entornos disponibles que están en tox.ini
- -r : Fuerza la re creación de los entornos virtuales
- --help: muestra la ayuda y muchas mas opciones que no están acá
¿Preguntas?
Gracias!
- github + twitter: @angvp
- código en: https://github.com/angvp/dpte/
- slides en: https://slides.com/angelvelasquez-1/deck-3/
django pytest tox y acción
By Angel Velásquez
django pytest tox y acción
Charla para el Django Meet-up BsAs de Diciembre 2015
- 720