Sök…


Testning - ett komplett exempel

Detta förutsätter att du har läst dokumentationen om att starta ett nytt Django-projekt. Låt oss anta att huvudprogrammet i ditt projekt heter td (kort för testdriven). För att skapa ditt första test skapar du en fil med namnet test_view.py och kopierar klistra in följande innehåll i den.

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)

Du kan köra detta test med

 ./manage.py test

och det kommer naturligtvis att misslyckas! Du kommer att se ett fel som liknar följande.

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

Varför händer det? Eftersom vi inte har definierat en vy för det! Så låt oss göra det. Skapa en fil som heter views.py och placera den i följande kod

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

Nästa kartlägg det till / hej / genom att redigera urls py enligt följande:

from td import views

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

./manage.py test nu testet igen ./manage.py test igen och viola !!

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

OK

Testa Django-modeller effektivt

Antagande en klass

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

Testa exempel

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

Några poäng

  • created_properly tester används för att verifiera django-modellens tillståndsegenskaper. De hjälper till att fånga platser där vi har ändrat standardvärden, file_upload_paths etc.
  • absolute_url kanske verkar trivial men jag har funnit att det har hjälpt mig att förhindra vissa buggar när jag ändrar url-banor
  • Jag skriver på liknande sätt testfall för alla metoder implementerade i en modell (med hjälp av mock objekt osv)
  • Genom att definiera ett gemensamt BaseModelTestCase vi ställa in nödvändiga förhållanden mellan modeller för att säkerställa korrekt testning.

Slutligen, när du är osäker, skriv ett test. Triviala beteendeförändringar fångas genom att uppmärksamma detaljer och länge glömda kodstycken ger inte onödiga problem.

Testa åtkomstkontroll i Django Views

tl; dr : Skapa en basklass som definierar två användarobjekt (säger user och en another_user user ). Skapa dina andra modeller och definiera tre Client .

  • self.client : Representerar user webbläsare
  • self.another_client : Representerar en another_user klient
  • self.unlogged_client : Representerar ologgad person

Nu får du tillgång till alla dina offentliga och privata webbadresser från dessa tre klientobjekt och dictact det svar du förväntar dig. Nedan har jag lyfte strategin för en Book objekt som antingen kan vara private (som ägs av ett fåtal privilegierade användare) eller public (synlig för alla).

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)

Databasen och testningen

Django använder speciella databasinställningar vid testning så att tester kan använda databasen normalt men som standard köras på en tom databas. Databasändringar i ett test kommer inte att ses av ett annat. Till exempel kommer båda följande test att klara:

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)

fixturer

Om du vill ha databasobjekt som används av flera test, skapar du dem setUp metoden setUp i testfallet. Om du har definierat fixturer i ditt django-projekt kan de dessutom inkluderas så:

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

Som standard letar django efter inventarier i katalogen för fixtures i varje app. Ytterligare kataloger kan ställas in med FIXTURE_DIRS inställningen:

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

Låt oss anta att du har skapat en modell enligt följande:

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

Då kan dina .json-fixturer se ut så:

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

Återanvänd testdatabasen

För att påskynda dina testkörningar kan du säga ledningskommandot att återanvända testdatabasen (och förhindra att den skapas före och raderas efter varje testkörning). Detta kan göras med keepdb (eller stenografi- -k ) -flaggan så:

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

Begränsa antalet utförda tester

Det är möjligt att begränsa de tester som utförs av manage.py test genom att specificera vilka moduler som ska upptäckas av testrunner:

# 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

Om du vill köra ett antal tester kan du klara ett mönster med filnamn. Till exempel kanske du vill köra bara tester som involverar dina modeller:

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

OK

Slutligen är det möjligt att stoppa testsviten vid första misslyckandet med - --failfast . Detta argument gör det möjligt att snabbt få det potentiella felet i sviten:

$ 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
Licensierat under CC BY-SA 3.0
Inte anslutet till Stack Overflow