[PYTHON] Django Tutorial (Blog App erstellen) ④ --Einheitentest

Das letzte Mal ist Django Tutorial (Blog-App erstellen) ③ - Artikellistenanzeige eine klassenbasierte Allzweckansicht zum Anzeigen einer Liste von Artikeln, die auf der Verwaltungssite erstellt wurden. Ich benutzte.

Ich möchte die CRUD-Verarbeitung wie Artikelerstellung, Details, Bearbeitung und Löschung in der App so wie sie ist hinzufügen, aber lassen Sie uns zurückhalten und ** Unit Test ** einschließen.

Über Django-Tests

Es macht Spaß, weitere Funktionen hinzuzufügen, aber schreiben Sie normalerweise Tests?

Sogar diejenigen, die in der Lage sind, einfache Django-Apps durch verschiedene Tutorials usw. zu erstellen. Ich denke, dass es einen Fehler verursachen kann, wenn Sie ein wenig damit spielen. Auch wenn beim Starten von Django mit Runserver usw. kein Fehler ausgegeben wird. Möglicherweise stellen Sie einen Fehler fest, wenn Sie den Bildschirm tatsächlich über den Browser verschieben.

Natürlich gibt es eine Möglichkeit, einige Vorgänge manuell zu testen, aber es ist jedes Mal verschwenderisch, dies zu tun.

Daher wird empfohlen, einen Komponententest mit der Django-Funktion durchzuführen. Mit Django können Sie Tests mithilfe der UnitTest-Klasse automatisieren Sobald Sie zuerst nur den Testcode geschrieben haben, müssen Sie dies nicht immer wieder tun.

Das Nachdenken über das Testen ist genauso wichtig wie das Nachdenken über Entwicklungscode. Es gibt sogar eine Entwicklungsmethode, um einen Test zu erstellen und dann den Code für den Betrieb der Anwendung zu schreiben.

Jetzt, da Sie testen können, sparen Sie Testzeit und arbeiten hart daran, die App selbst zu verbessern.

Informationen zur Ordnerstruktur

Zu diesem Zeitpunkt sollte die Ordnerstruktur wie folgt aussehen.

.
├── blog
│   ├── __init__.py
│   ├── admin.py
│   ├── apps.py
│   ├── migrations
│   │   ├── 0001_initial.py
│   │   └── __init__.py
│   ├── models.py
│   ├── tests.py #Beachtung
│   ├── urls.py
│   └── views.py
├── db.sqlite3
├── manage.py
├── mysite
│   ├── __init__.py
│   ├── settings.py
│   ├── urls.py
│   └── wsgi.py
└── templates
    └── blog
        ├── index.html
        └── post_list.html

Wie Sie vielleicht bemerkt haben, wird automatisch eine Datei mit dem Namen ** tests.py ** im Blog-Verzeichnis erstellt.

Sie können Testfälle direkt in dieser tests.py erstellen. Es ist einfacher zu verwalten, ob die Dateien für jeden Modelltest, Ansichtstest und Test getrennt sind. Erstellen Sie ein Testverzeichnis wie unten gezeigt und erstellen Sie jeweils eine leere Datei. Der Punkt ist, eine ** __ init__.py ** -Datei aus dem Inhalt zu erstellen, damit auch die Dateien im Testverzeichnis ausgeführt werden.

.
├── blog
│   ├── __init__.py
│   ├── admin.py
│   ├── apps.py
│   ├── migrations
│   │   ├── 0001_initial.py
│   │   └── __init__.py
│   ├── models.py
│   ├── tests #hinzufügen
│   │   ├── __init__.py
│   │   ├── test_models.py
│   │   ├── test_urls.py
│   │   └── test_views.py
......

Bitte beachten Sie, dass Django den Modulnamen nur erkennt, wenn er mit "test" beginnt.

Wie schreibe ich einen Test?

Django erweitert die Python-Standard-TestCase-Klasse (unittest.TestCase). Verwenden Sie Djangos eigene TestCase-Klasse (django.test.TestCase). In dieser Klasse können Sie eine Methode namens assertion verwenden, mit der überprüft wird, ob der Rückgabewert der erwartete Wert ist.

Wie oben erwähnt, muss das Testmodul mit der Zeichenfolge "test" beginnen. Die Testmethode muss auch mit der Zeichenfolge "test" beginnen (dazu später mehr).

Wenn Sie diese Regel befolgen, findet Django die Testmethode in Ihrem Projekt und führt sie automatisch aus.

test_models.py Beginnen wir mit dem Testen des Modells. Zusammenfassend sieht das in blog / models.py beschriebene Post-Modell so aus.

models.py


...

class Post(models.Model):
    title = models.CharField('Titel', max_length=200)
    text = models.TextField('Text')
    date = models.DateTimeField('Datum', default=timezone.now)

    def __str__(self): #Definiert den Wert, der zurückgegeben werden soll, wenn das Post-Modell direkt aufgerufen wird
        return self.title #Gibt den Titel des Artikels zurück

Testen wir dieses Modell diesmal mit den folgenden drei Fällen.

  1. Im Ausgangszustand ist nichts registriert
  2. Wenn Sie einen Datensatz ordnungsgemäß erstellen, wird nur ein Datensatz gezählt.
  3. Speichern Sie die Daten mit dem angegebenen Inhalt. Wenn Sie sie sofort herausnehmen, wird derselbe Wert wie beim Speichern zurückgegeben.

Dann vom ersten.

Öffnen Sie test_models.py und deklarieren Sie die erforderlichen Module.

test_models.py


from django.test import TestCase
from blog.models import Post

Dann erstellen wir eine Testklasse, stellen jedoch sicher, dass es sich um eine Klasse handelt, die TestCase erbt.

test_models.py


from django.test import TestCase
from blog.models import Post

class PostModelTests(TestCase):

Schreiben wir nun eine Testmethode in diese PostModelTest-Klasse. Beginnen Sie mit "test" in einer Klasse, die TestCase erbt Django erkennt automatisch, dass es sich um eine Testmethode handelt. Benennen Sie daher die Methode, die mit test beginnt, nach def.

test_models.py


from django.test import TestCase
from blog.models import Post

class PostModelTests(TestCase):

  def test_is_empty(self):
      """Überprüfen Sie, ob im Ausgangszustand nichts registriert ist"""  
      saved_posts = Post.objects.all()
      self.assertEqual(saved_posts.count(), 0)

Speichern Sie das aktuelle Post-Modell in saved_posts Wir haben bestätigt, dass die Anzahl (Anzahl der Artikel) mit assertEqual "0" ist.

Jetzt können Sie einen Test durchführen. Lassen Sie es uns einmal damit ausführen.

Führen Sie zum Ausführen des Tests den folgenden Befehl in dem Verzeichnis (auf mysite) aus, in dem sich manage.py befindet. Wenn Sie es ausführen, findet Django eine Testmethode und führt sie aus, die der Namenskonvention folgt.

(blog) bash-3.2$ python3 manage.py test
Creating test database for alias 'default'...
System check identified no issues (0 silenced).
.....
----------------------------------------------------------------------
Ran 1 tests in 0.009s

OK

Dies bedeutet, dass Sie einen Test ausgeführt und fehlerfrei abgeschlossen haben.

Übrigens habe ich früher in Post bestätigt, dass die Daten leer sind (= 0), aber erwarten wir, dass es nur eine Daten gibt.

test_models.py(vorübergehend)


from django.test import TestCase
from blog.models import Post

class PostModelTests(TestCase):

  def test_is_empty(self):
      """Der Ausgangszustand besteht jedoch darin, zu überprüfen, ob Daten vorhanden sind(Fehler wird erwartet)"""  
      saved_posts = Post.objects.all()
      self.assertEqual(saved_posts.count(), 1)

Das Ergebnis der Testausführung zu diesem Zeitpunkt ist wie folgt.

(blog) bash-3.2$ python3 manage.py test
Creating test database for alias 'default'...
System check identified no issues (0 silenced).
F
======================================================================
FAIL: test_is_empty (blog.tests.test_models.PostModelTests)
Der Ausgangszustand besteht jedoch darin, zu überprüfen, ob Daten vorhanden sind(Fehler wird erwartet)
----------------------------------------------------------------------
Traceback (most recent call last):
  File "/Users/masuyama/workspace/MyPython/MyDjango/blog/mysite/blog/tests/test_models.py", line 9, in test_is_empty
    self.assertEqual(saved_posts.count(), 1)
AssertionError: 0 != 1

----------------------------------------------------------------------
Ran 1 test in 0.002s

FAILED (failures=1)

Sie erhalten einen AssertionError und der Test schlägt fehl, weil er nicht Ihren Erwartungen entspricht (es ist ein erfolgreiches Experiment).

In Djangos Test können Sie also auch vorübergehend Daten aus der create-Methode in der Datenbank registrieren Sie können auch den Rest der Tests ausführen, die Sie ohne Registrierung der Daten nicht sehen könnten. Im Folgenden erfahren Sie, wie Sie einen Modelltest schreiben. Lesen Sie ihn daher bitte.

test_models.py(Voller Text)


from django.test import TestCase
from blog.models import Post

class PostModelTests(TestCase):

  def test_is_empty(self):
    """Überprüfen Sie, ob im Ausgangszustand nichts registriert ist"""  
    saved_posts = Post.objects.all()
    self.assertEqual(saved_posts.count(), 0)
  
  def test_is_count_one(self):
    """Testen Sie, ob nur ein Datensatz gezählt wird, wenn Sie einen Datensatz ordnungsgemäß erstellen"""
    post = Post(title='test_title', text='test_text')
    post.save()
    saved_posts = Post.objects.all()
    self.assertEqual(saved_posts.count(), 1)

  def test_saving_and_retrieving_post(self):
    """Speichern Sie die Daten mit dem angegebenen Inhalt und testen Sie, ob derselbe Wert wie beim Speichern zurückgegeben wird, wenn er sofort entfernt wird"""
    post = Post()
    title = 'test_title_to_retrieve'
    text = 'test_text_to_retrieve'
    post.title = title
    post.text = text
    post.save()

    saved_posts = Post.objects.all()
    actual_post = saved_posts[0]

    self.assertEqual(actual_post.title, title)
    self.assertEqual(actual_post.text, text)

test_urls.py Neben dem Modell können Sie auch überprüfen, ob das in urls.py geschriebene Routing funktioniert. Rückblickend sah blog / urls.py so aus.

blog/urls.py


from django.urls import path
from . import views

app_name = 'blog'

urlpatterns = [
    path('', views.IndexView.as_view(), name='index'),
    path('list', views.PostListView.as_view(), name='list'),
]

Im obigen Routing wird das Routing entsprechend der unter / blog / eingegebenen Adresse eingestellt / blog / Teste, wenn die folgenden '' (leer) und 'list' sind. Verwenden Sie assertEqual, um zu vergleichen und zu überprüfen, ob die Ergebnisse erwartet werden, die über die Ansicht auf jede Seite umgeleitet werden.

test_urls.py


from django.test import TestCase
from django.urls import reverse, resolve
from ..views import IndexView, PostListView

class TestUrls(TestCase):

  """Getestete Umleitung beim Zugriff per URL auf die Indexseite"""
  def test_post_index_url(self):
    view = resolve('/blog/')
    self.assertEqual(view.func.view_class, IndexView)

  """Testumleitung zur Seite "Liste veröffentlichen""""
  def test_post_list_url(self):
    view = resolve('/blog/list')
    self.assertEqual(view.func.view_class, PostListView)

Lassen Sie uns den Test jetzt einmal ausführen.

(blog) bash-3.2$ python3 manage.py test
Creating test database for alias 'default'...
System check identified no issues (0 silenced).
.....
----------------------------------------------------------------------
Ran 5 tests in 0.007s

OK
Destroying test database for alias 'default'...

test_views.py Zum Schluss testen wir die Ansicht.

views.py sah so aus.

views.py


from django.views import generic
from .models import Post  #Post-Modell importieren

class IndexView(generic.TemplateView):
    template_name = 'blog/index.html'

class PostListView(generic.ListView): #Erben Sie die generische ListView-Klasse
    model = Post #Rufen Sie das Modell auf, das Sie auflisten möchten

Der IndexView-Test überprüft, ob der Statuscode 200 (= Erfolg) zurückgegeben wird, wenn auf die GET-Methode zugegriffen wird.

test_views.py


from django.test import TestCase
from django.urls import reverse

from ..models import Post

class IndexTests(TestCase):
  """IndexView-Testklasse"""

  def test_get(self):
    """Bestätigen Sie, dass der Statuscode 200 zurückgegeben wird, indem Sie mit der GET-Methode zugreifen."""
    response = self.client.get(reverse('blog:index'))
    self.assertEqual(response.status_code, 200)

Wenn Sie in einer beliebigen Ansicht eine Methode hinzufügen, Gewöhnen Sie sich an, zumindest dies als Testfall zu erstellen, egal wie viel Zeit Sie zum Schreiben eines Tests benötigen.

Wir werden auch die ListView testen.

Ganz zu schweigen von der Bestätigung, dass der Statuscode 200 ebenfalls zurückgegeben wird. Hier wird nach dem Hinzufügen von zwei Daten (Artikeln) die Artikelliste angezeigt und Erstellen Sie einen Test, um sicherzustellen, dass jeder der registrierten Artikeltitel in der Liste enthalten ist.

Beachten Sie, dass wir hier eine etwas spezielle Methode verwenden werden. Ich habe bereits erwähnt, dass die Testmethode mit "test" beginnt, aber es gibt Methoden ** setUp ** und ** tearDown **.

Registrieren Sie in der setUp-Methode die im Testfall und verwendeten Daten Mit der TearDown-Methode können Sie die in der setUp-Methode registrierten Daten löschen. (Beachten Sie, dass beide explizit angeben müssen, welche Daten registriert werden sollen.)

Das Schreiben eines Prozesses, der Daten viele Male im selben Testfall registriert, ist ein Faktor, dessen Testen Zeit und Zeit in Anspruch nimmt. Der übliche Prozess besteht darin, sie an einem Ort zusammenzufügen.

Wenn Sie diese Methoden zum Erstellen von test_views.py verwenden, sieht dies folgendermaßen aus.

test_views.py


from django.test import TestCase
from django.urls import reverse

from ..models import Post

class IndexTests(TestCase):
  """IndexView-Testklasse"""

  def test_get(self):
    """Bestätigen Sie, dass der Statuscode 200 zurückgegeben wird, indem Sie mit der GET-Methode zugreifen."""
    response = self.client.get(reverse('blog:index'))
    self.assertEqual(response.status_code, 200)

class PostListTests(TestCase):

  def setUp(self):
    """
Eine Methode zur Vorbereitung der Testumgebung. Nennen Sie es unbedingt "setUp".
Wenn es Daten gibt, die Sie innerhalb derselben Testklasse gemeinsam verwenden möchten, erstellen Sie sie hier.
    """
    post1 = Post.objects.create(title='title1', text='text1')
    post2 = Post.objects.create(title='title2', text='text2')

  def test_get(self):
    """Bestätigen Sie, dass der Statuscode 200 zurückgegeben wird, indem Sie mit der GET-Methode zugreifen."""
    response = self.client.get(reverse('blog:list'))
    self.assertEqual(response.status_code, 200)
  
  def test_get_2posts_by_list(self):
    """Stellen Sie sicher, dass beim Zugriff mit GET 2 zusätzliche Elemente zurückgegeben werden, die von der setUp-Methode hinzugefügt wurden"""
    response = self.client.get(reverse('blog:list'))
    self.assertEqual(response.status_code, 200)
    self.assertQuerysetEqual(
      #Im Post-Modell__str__Überprüfen Sie, ob der zurückgegebene Titel wie gebucht ist, da der Titel als Ergebnis von zurückgegeben wird
      response.context['post_list'],
      ['<Post: title1>', '<Post: title2>'],
      ordered = False #Geben Sie an, dass die Reihenfolge ignoriert werden soll
    )
    self.assertContains(response, 'title1') #Stellen Sie sicher, dass der Titel post1 im HTML-Code enthalten ist
    self.assertContains(response, 'title2') #Stellen Sie sicher, dass der Titel post2 im HTML-Code enthalten ist

  def tearDown(self):
      """
Eine Reinigungsmethode, mit der die von setUp hinzugefügten Daten gelöscht werden.
Obwohl es sich um "create" handelt, wird durch Setzen des Methodennamens auf "tearDown" die umgekehrte Verarbeitung von setUp ausgeführt = es wird gelöscht.
      """
      post1 = Post.objects.create(title='title1', text='text1')
      post2 = Post.objects.create(title='title2', text='text2')

Wenn Sie den Test in diesem Status ausführen, werden insgesamt 8 Tests für Modell, URL und Ansicht ausgeführt.

(blog) bash-3.2$ python3 manage.py test
Creating test database for alias 'default'...
System check identified no issues (0 silenced).
........
----------------------------------------------------------------------
Ran 8 tests in 0.183s

OK
Destroying test database for alias 'default'...

Sie haben jetzt einen Komponententest für den Code erstellt, den Sie bisher geschrieben haben. Ob andere erwartete Vorlagen aufgerufen werden usw. Es gibt auch eine Möglichkeit, mit einem Test redundant zu prüfen, indem Djangos eigene Testmethode verwendet wird. Gewöhnen Sie sich an, Tests zu schreiben, bevor Sie Code schreiben, und sparen Sie sich die Mühe, sie später zu überprüfen.

Nächstes Mal kann ich Artikel in der App erstellen. Lassen Sie uns mit dem TDD-Stil (Test Driven Development) fortfahren, den Unit-Test zu schreiben, den wir diesmal zuerst gelernt haben.

Recommended Posts

Django Tutorial (Blog App erstellen) ④ --Einheitentest
Django Tutorial (Blog-App erstellen) ⑤ - Artikelerstellungsfunktion
Django Tutorial (Blog App erstellen) ① - Vorbereitung, Erstellung der obersten Seite
Django Tutorial (Blog App erstellen) ③ - Artikellistenanzeige
Django Tutorial (Blog App erstellen) ⑦ --Front End Complete
Django-Tutorial (Blog-App erstellen) Ar - Artikeldetails / Funktionen bearbeiten / löschen
Django-Tutorial (Erstellung von Blog-Apps) ② - Modellerstellung, Vorbereitung der Verwaltungssite
Django Tutorial Zusammenfassung für Anfänger von Anfängern ⑤ (Test)
Python Django Tutorial (5)
Python Django Tutorial (2)
Django-Tabellenerstellung
Numpy Unit Test
Django Tutorial Memo
Django Tutorial Zusammenfassung für Anfänger von Anfängern ① (Projekterstellung ~)
Starten Sie das Django Tutorial 1
Python Django Tutorial (1)
Python Django Tutorial Tutorial
Python Django Tutorial (3)
Python Django Tutorial (4)
Was ist ein Hund? Startvolumen der Django-App erstellen --startapp
Was ist ein Hund? Django App Creation Start Volume - Startprojekt
[Django] Neue Funktion zum Erstellen von Fragen zur Umfrage-App hinzugefügt
Schreiben Sie Code in UnitTest, eine Python-Webanwendung
Zusammenfassung des Python Django-Tutorials
Starten Sie meine Django-App
Django Polymorphic Associations Tutorial
Initialisieren Sie die Django-App
Django Oscar einfaches Tutorial
Django Shift Creation Funktion
Django Girls Tutorial Hinweis
Führen Sie einen Komponententest des Databricks Notebook durch
Python-Unit-Test-Vorlage