Ricerca…


Test: un esempio completo

Questo presuppone che tu abbia letto la documentazione sull'avvio di un nuovo progetto Django. Supponiamo che l'app principale del progetto sia denominata td (abbreviazione di testing driven). Per creare il tuo primo test, crea un file denominato test_view.py e copia incolla il seguente contenuto in esso.

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)

Puoi eseguire questo test con

 ./manage.py test

e molto probabilmente fallirà! Verrà visualizzato un errore simile al seguente.

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

Perché succede? Perché non abbiamo definito una visione per questo! Facciamolo. Crea un file chiamato views.py e inserisci il seguente codice

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

Quindi mappalo al / ciao / modificando gli url py come segue:

from td import views

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

Ora esegui nuovamente il test ./manage.py test nuovo e viola !!

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

OK

Testare i modelli Django in modo efficace

Supponendo una lezione

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

Esempi di prova

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

Alcuni punti

  • created_properly test created_properly vengono utilizzati per verificare le proprietà di stato dei modelli django. Aiutano a catturare le sitauzioni dove abbiamo cambiato i valori predefiniti, file_upload_paths ecc.
  • absolute_url potrebbe sembrare banale ma ho scoperto che mi ha aiutato a prevenire alcuni bug durante la modifica dei percorsi di url
  • Analogamente, scrivo casi di test per tutti i metodi implementati all'interno di un modello (usando oggetti mock , ecc.)
  • Definendo un BaseModelTestCase comune possiamo configurare le relazioni necessarie tra i modelli per garantire un test corretto.

Infine, in caso di dubbio, scrivi un test. I cambiamenti del comportamento banale vengono catturati prestando attenzione ai dettagli e lunghi pezzi di codice dimenticati non finiscono per causare problemi inutili.

Testare il controllo degli accessi nelle viste Django

tl; dr : crea una classe base che definisce due oggetti utente (ad esempio user e another_user ). Crea gli altri tuoi modelli e definisci tre istanze del Client .

  • self.client : rappresenta l' user effettuato user accesso al browser
  • self.another_client : Rappresentare another_user cliente 's
  • self.unlogged_client : rappresenta la persona non self.unlogged_client

Ora accedi a tutti i tuoi URL pubblici e privati ​​da questi tre oggetti client e imposta la risposta che ti aspetti. Di seguito ho mostrato la strategia per un oggetto Book che può essere private (di proprietà di pochi utenti privilegiati) o public (visibile a tutti).

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)

Il database e i test

Django utilizza impostazioni di database speciali durante i test in modo che i test possano utilizzare normalmente il database, ma per impostazione predefinita vengono eseguiti su un database vuoto. Le modifiche al database in un test non saranno viste da un altro. Ad esempio, entrambi i seguenti test supereranno:

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)

infissi

Se si desidera disporre di oggetti di database utilizzati da più test, crearli nel metodo setUp del caso di test. Inoltre, se hai definito fixtures nel tuo progetto django, possono essere inclusi in questo modo:

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

Per impostazione predefinita, django è alla ricerca di fixture nella directory fixtures in ogni app. Ulteriori directory possono essere impostate utilizzando l'impostazione FIXTURE_DIRS :

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

Supponiamo che tu abbia creato un modello come segue:

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

Quindi i tuoi apparecchi .json potrebbero assomigliare a questo:

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

Riutilizzare il database di test

Per accelerare le esecuzioni dei test, è possibile indicare al comando di gestione di riutilizzare il database di test (e impedire che venga creato prima e eliminato dopo ogni esecuzione di prova). Questo può essere fatto usando il flag keepdb (o shorthand -k ) in questo modo:

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

Limita il numero di test eseguiti

È possibile limitare i test eseguiti dal manage.py test specificando quali moduli devono essere rilevati dal test runner:

# 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

Se vuoi eseguire una serie di test, puoi passare uno schema di nomi di file. Ad esempio, potresti voler eseguire solo test che coinvolgono i tuoi modelli:

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

OK

Infine, è possibile interrompere la suite di test al primo errore, usando --failfast . Questo argomento consente di ottenere rapidamente il potenziale errore riscontrato nella 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
Autorizzato sotto CC BY-SA 3.0
Non affiliato con Stack Overflow