Buscar..


Pruebas - un ejemplo completo

Esto supone que ha leído la documentación sobre cómo iniciar un nuevo proyecto de Django. Supongamos que la aplicación principal de su proyecto se llama td (abreviatura de prueba). Para crear su primera prueba, cree un archivo llamado test_view.py y copie y pegue el siguiente contenido en él.

from django.test import Client, TestCase

class ViewTest(TestCase):

    def test_hello(self):
        c = Client()
        resp = c.get('/hello/')
        self.assertEqual(resp.status_code, 200)

Puede ejecutar esta prueba por

 ./manage.py test

¡Y fallará naturalmente! Verás un error similar al siguiente.

Traceback (most recent call last):
  File "/home/me/workspace/td/tests_view.py", line 9, in test_hello
    self.assertEqual(resp.status_code, 200)
AssertionError: 200 != 404

¿Por qué sucede eso? ¡Porque no hemos definido una vista para eso! Hagamoslo. Cree un archivo llamado views.py y coloque en él el siguiente código

from django.http import HttpResponse
def hello(request):
    return HttpResponse('hello')

Luego mapéelo a / hello / editando las direcciones URL como sigue:

from td import views

urlpatterns = [
    url(r'^admin/', include(admin.site.urls)),
    url(r'^hello/', views.hello),
    ....
]

Ahora ejecuta la prueba otra vez ./manage.py test otra vez y viola !!

Creating test database for alias 'default'...
.
----------------------------------------------------------------------
Ran 1 test in 0.004s

OK

Probando Modelos Django Efectivamente

Asumiendo una clase

from django.db import models

class Author(models.Model):
   name = models.CharField(max_length=50)
   
    def __str__(self):
        return self.name
    
    def get_absolute_url(self):
        return reverse('view_author', args=[str(self.id)])


class Book(models.Model):
    author = models.ForeignKey(Manufacturer, on_delete=models.CASCADE)
    private = models.BooleanField(default=false)
    publish_date = models.DateField()

    def get_absolute_url(self):
        return reverse('view_book', args=[str(self.id)])
    
    def __str__(self):
        return self.name

Ejemplos de prueba

from django.test import TestCase
from .models import Book, Author

class BaseModelTestCase(TestCase):

    @classmethod
    def setUpClass(cls):
        super(BaseModelTestCase, cls).setUpClass()
        cls.author = Author(name='hawking')
        cls.author.save()
        cls.first_book = Book(author=cls.author, name="short_history_of_time")
        cls.first_book.save()
        cls.second_book = Book(author=cls.author, name="long_history_of_time")
        cls.second_book.save()


class AuthorModelTestCase(BaseModelTestCase):
    def test_created_properly(self):
         self.assertEqual(self.author.name, 'hawking')
         self.assertEqual(True, self.first_book in self.author.book_set.all())
    
    def test_absolute_url(self):
        self.assertEqual(self.author.get_absolute_url(), reverse('view_author', args=[str(self.author.id)]))

class BookModelTestCase(BaseModelTestCase):
    
    def test_created_properly(self:
        ...
        self.assertEqual(1, len(Book.objects.filter(name__startswith='long'))
    
    def test_absolute_url(self):
        ...

Algunos puntos

  • created_properly pruebas created_properly se utilizan para verificar las propiedades de estado de los modelos de django. Ayudan a detectar situaciones en las que hemos cambiado los valores predeterminados, file_upload_paths, etc.
  • absolute_url puede parecer trivial, pero he descubierto que me ayudó a evitar algunos errores al cambiar las rutas de URL
  • De manera similar, escribo casos de prueba para todos los métodos implementados dentro de un modelo (utilizando objetos mock , etc.)
  • Al definir una BaseModelTestCase común, podemos configurar las relaciones necesarias entre los modelos para garantizar una prueba adecuada.

Finalmente, ante la duda, escribe una prueba. Los cambios de comportamiento triviales se captan prestando atención a los detalles y los fragmentos de código olvidados no terminan causando problemas innecesarios.

Pruebas de control de acceso en Django Views

tl; dr : crea una clase base que define dos objetos de usuario (por ejemplo, user y another_user ). Crea tus otros modelos y define tres instancias de Client .

  • self.client : Representando user registrado en el navegador
  • self.another_client : Representando another_user cliente de another_user
  • self.unlogged_client : Representa a una persona no registrada

Ahora acceda a todas sus direcciones URL públicas y privadas desde estos tres objetos de cliente y dicte la respuesta que espera. A continuación, muestro la estrategia para un objeto Book que puede ser private (propiedad de unos pocos usuarios privilegiados) o public (visible para todos).

from django.test import TestCase, RequestFactory, Client
from django.core.urlresolvers import reverse

class BaseViewTestCase(TestCase):

    @classmethod
    def setUpClass(cls):
        super(BaseViewTestCase, cls).setUpClass()
        cls.client = Client()
        cls.another_client = Client()
        cls.unlogged_client = Client()
        cls.user = User.objects.create_user(
                'dummy',password='dummy'
                )
        cls.user.save()
        cls.another_user = User.objects.create_user(
                'dummy2', password='dummy2'
                )
        cls.another_user.save()
        cls.first_book = Book.objects.create(
                name='first',
                private = true
        )
        cls.first_book.readers.add(cls.user)
        cls.first_book.save()
        cls.public_book = Template.objects.create(
                name='public',
                private=False
        )
        cls.public_book.save()


    def setUp(self):
        self.client.login(username=self.user.username, password=self.user.username)
        self.another_client.login(username=self.another_user.username, password=self.another_user.username)


"""
   Only cls.user owns the first_book and thus only he should be able to see it.
   Others get 403(Forbidden) error
"""
class PrivateBookAccessTestCase(BaseViewTestCase):
    
    def setUp(self):
        super(PrivateBookAccessTestCase, self).setUp()
        self.url = reverse('view_book',kwargs={'book_id':str(self.first_book.id)})

    def test_user_sees_own_book(self):
        response = self.client.get(self.url)
        self.assertEqual(200, response.status_code)
        self.assertEqual(self.first_book.name,response.context['book'].name)
        self.assertTemplateUsed('myapp/book/view_template.html')

    def test_user_cant_see_others_books(self):
        response = self.another_client.get(self.url)
        self.assertEqual(403, response.status_code)
        
    def test_unlogged_user_cant_see_private_books(self):
        response = self.unlogged_client.get(self.url)
        self.assertEqual(403, response.status_code)

"""
    Since book is public all three clients should be able to see the book
"""
 class PublicBookAccessTestCase(BaseViewTestCase):
    
    def setUp(self):
        super(PublicBookAccessTestCase, self).setUp()
        self.url = reverse('view_book',kwargs={'book_id':str(self.public_book.id)})

    def test_user_sees_book(self):
        response = self.client.get(self.url)
        self.assertEqual(200, response.status_code)
        self.assertEqual(self.public_book.name,response.context['book'].name)
        self.assertTemplateUsed('myapp/book/view_template.html')

    def test_another_user_sees_public_books(self):
        response = self.another_client.get(self.url)
        self.assertEqual(200, response.status_code)
        
    def test_unlogged_user_sees_public_books(self):
        response = self.unlogged_client.get(self.url)
        self.assertEqual(200, response.status_code)

La base de datos y las pruebas

Django usa una configuración especial de la base de datos cuando realiza pruebas, de modo que las pruebas pueden usar la base de datos normalmente, pero se ejecutan por defecto en una base de datos vacía. Los cambios en la base de datos en una prueba no serán vistos por otra. Por ejemplo, ambas de las siguientes pruebas pasarán:

from django.test import TestCase
from myapp.models import Thing

class MyTest(TestCase):

    def test_1(self):
        self.assertEqual(Thing.objects.count(), 0)
        Thing.objects.create()
        self.assertEqual(Thing.objects.count(), 1)

    def test_2(self):
        self.assertEqual(Thing.objects.count(), 0)
        Thing.objects.create(attr1="value")
        self.assertEqual(Thing.objects.count(), 1)

Accesorios

Si desea que los objetos de la base de datos utilicen varias pruebas, puede crearlos en el método de setUp del caso de prueba. Además, si ha definido los accesorios en su proyecto de django, se pueden incluir así:

class MyTest(TestCase):
    fixtures = ["fixture1.json", "fixture2.json"]

Por defecto, django está buscando accesorios en el directorio de fixtures en cada aplicación. Se pueden configurar otros directorios utilizando la configuración FIXTURE_DIRS :

# myapp/settings.py
FIXTURE_DIRS = [
    os.path.join(BASE_DIR, 'path', 'to', 'directory'),
]

Supongamos que ha creado un modelo de la siguiente manera:

# models.py
from django.db import models


class Person(models.Model):
    """A person defined by his/her first- and lastname."""
    firstname = models.CharField(max_length=255)
    lastname = models.CharField(max_length=255)

Entonces tus accesorios .json podrían verse así:

# fixture1.json
[
    { "model": "myapp.person",
        "pk": 1,
        "fields": {
            "firstname": "Peter",
            "lastname": "Griffin"
        }
    },
    { "model": "myapp.person",
        "pk": 2,
        "fields": {
            "firstname": "Louis",
            "lastname": "Griffin"
        }
    },
]

Reutilizar la base de datos de prueba.

Para acelerar las pruebas de ejecución, puede indicar al comando de administración que reutilice la base de datos de prueba (y que evite que se cree antes y se elimine después de cada ejecución de prueba). Esto se puede hacer usando la marca keepdb (o taquigrafía -k ) de esta manera:

# Reuse the test-database (since django version 1.8)
$ python manage.py test --keepdb

Limitar el número de pruebas ejecutadas.

Es posible limitar las pruebas ejecutadas por manage.py test especificando qué módulos debe descubrir el corredor de prueba:

# Run only tests for the app names "app1"
$ python manage.py test app1

# If you split the tests file into a module with several tests files for an app
$ python manage.py test app1.tests.test_models

# it's possible to dig down to individual test methods.
$ python manage.py test app1.tests.test_models.MyTestCase.test_something

Si desea ejecutar un montón de pruebas, puede pasar un patrón de nombres de archivos. Por ejemplo, es posible que desee ejecutar solo pruebas que involucren a sus modelos:

$ python manage.py test -p test_models*
Creating test database for alias 'default'...
.................................................
----------------------------------------------------------------------
Ran 115 tests in 3.869s

OK

Finalmente, es posible detener el conjunto de pruebas en el primer fallo, utilizando --failfast . Este argumento permite obtener rápidamente el error potencial encontrado en la suite:

$ python manage.py test app1
...F..
----------------------------------------------------------------------
Ran 6 tests in 0.977s

FAILED (failures=1)


$ python manage.py test app1 --failfast
...F
======================================================================
[Traceback of the failing test]
----------------------------------------------------------------------
Ran 4 tests in 0.372s

FAILED (failures=1)


Modified text is an extract of the original Stack Overflow Documentation
Licenciado bajo CC BY-SA 3.0
No afiliado a Stack Overflow