Zoeken…


Testen - een compleet voorbeeld

Dit veronderstelt dat u de documentatie hebt gelezen over het starten van een nieuw Django-project. Laten we aannemen dat de hoofdapp in uw project de naam td heeft (een afkorting voor testgestuurd). Maak om uw eerste test te maken een bestand met de naam test_view.py en kopieer en plak de volgende inhoud erin.

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)

U kunt deze test uitvoeren door

 ./manage.py test

en het zal natuurlijk falen! U zult een fout zien die lijkt op het volgende.

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

Waarom gebeurt dat? Omdat we daar geen zicht op hebben gedefinieerd! Dus laten we het doen. Maak een bestand met de naam views.py en plaats daarin de volgende code

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

Wijs het vervolgens toe aan de / hallo / door urls py als volgt te bewerken:

from td import views

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

Voer de test nu opnieuw uit ./manage.py test opnieuw en altviool !!

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

OK

Django-modellen effectief testen

Een klasse aannemen

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

Voorbeelden van testen

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

Enkele punten

  • created_properly tests worden gebruikt om de staatseigenschappen van django-modellen te verifiëren. Ze helpen sitautions te vangen waar we standaardwaarden, file_upload_paths etc. hebben gewijzigd.
  • absolute_url lijkt misschien triviaal, maar ik heb ontdekt dat het me heeft geholpen bij het voorkomen van enkele bugs bij het wijzigen van URL-paden
  • Ik schrijf op dezelfde manier testgevallen voor alle methoden die in een model zijn geïmplementeerd (met behulp van mock , enz.)
  • Door een gemeenschappelijke BaseModelTestCase definiëren, kunnen we de nodige relaties tussen modellen instellen om een goede test te garanderen.

Eindelijk, bij twijfel, schrijf een test. Triviale gedragsveranderingen worden opgevangen door aandacht te besteden aan details en lang vergeten stukjes code veroorzaken geen onnodige problemen.

Toegangscontrole testen in Django-weergaven

tl; dr : Maak een basisklasse die twee gebruikersobjecten definieert (zeg user en een andere another_user ). Maak uw andere modellen en definieer drie Client .

  • self.client : Vertegenwoordiger van user ingelogde browser van de gebruiker
  • self.another_client : Vertegenwoordigen another_user 's-client
  • self.unlogged_client : Vertegenwoordiger van niet-geregistreerde persoon

Krijg nu toegang tot al uw openbare en privé-URL's van deze drie clientobjecten en dicteer de reactie die u verwacht. Hieronder heb ik de strategie uiteengezet voor een Book object dat private (eigendom van enkele bevoorrechte gebruikers) of public (voor iedereen zichtbaar) kan zijn.

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)

De database en testen

Django gebruikt speciale database-instellingen tijdens het testen, zodat tests de database normaal kunnen gebruiken, maar standaard op een lege database kunnen worden uitgevoerd. Databasewijzigingen in de ene test worden niet door een andere gezien. Beide van de volgende tests zullen bijvoorbeeld slagen:

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)

Wedstrijden

Als u wilt dat databaseobjecten door meerdere tests worden gebruikt, maakt u deze aan in de setUp methode van de testcase. Als u fixtures in uw django-project hebt gedefinieerd, kunnen deze bovendien als volgt worden opgenomen:

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

Standaard zoekt django naar armaturen in de map met fixtures in elke app. Verdere mappen kunnen worden ingesteld met de instelling FIXTURE_DIRS :

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

Laten we aannemen dat u een model als volgt hebt gemaakt:

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

Dan kunnen uw .json-armaturen er zo uitzien:

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

Gebruik de testdatabase opnieuw

Om uw testruns te versnellen, kunt u de management-opdracht vertellen de testdatabase opnieuw te gebruiken (en te voorkomen dat deze vóór elke testrun wordt gemaakt en verwijderd). Dit kan worden gedaan met behulp van de keepdb (of shorthand -k ) vlag als volgt:

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

Beperk het aantal uitgevoerde tests

Het is mogelijk om de tests die worden uitgevoerd door de manage.py test te beperken door op te geven welke modules door de manage.py test moeten worden ontdekt:

# 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

Als u een aantal tests wilt uitvoeren, kunt u een patroon met bestandsnamen doorstaan. U wilt bijvoorbeeld alleen tests uitvoeren die betrekking hebben op uw modellen:

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

OK

Ten slotte is het mogelijk om het testpakket te stoppen bij de eerste fout, met behulp van --failfast . Met dit argument kunt u snel de potentiële fout in de suite vinden:

$ 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
Licentie onder CC BY-SA 3.0
Niet aangesloten bij Stack Overflow