Dies ist eine Lernnotiz zum Verständnis von Test Driven Development (TDD) in Django.
Referenzen sind [** Testgetriebene Entwicklung mit Python: Befolgen Sie die Testziege: Verwenden von Django, Selen und JavaScript (englische Ausgabe) 2. Ausgabe **](https://www.amazon.co.jp/dp/B074HXXXLS Wir werden mit dem Lernen basierend auf / ref = dp-kindle-redirect? _ Encoding = UTF8 & btkr = 1) fortfahren.
In diesem Buch führen wir Funktionstests mit der Django 1.1-Serie und FireFox durch. Dieses Mal werden wir jedoch Funktionstests mit der Djagno 3-Serie und Google Chrome durchführen. Ich habe auch einige persönliche Änderungen vorgenommen (z. B. das Ändern des Projektnamens in Config), aber es gibt keine wesentlichen Änderungen.
⇒⇒ Klicken Sie hier für Teil 1 - Kapitel 1 ⇒⇒ Klicken Sie hier für Teil 2 - Kapitel 2
Part1. The Basics of TDD and Django
Capter3. Testing as Simple Home Page with Unit Tests
In Kapitel 2 haben wir einen Funktionstest mit unittest.TestCase
geschrieben und getestet, ob der Seitentitel ein" To-Do "enthält.
Dieses Mal starten wir die Anwendung und führen TDD durch.
Our First Django App, and Our First Unit Test
Django besteht darin, mehrere Anwendungen unter einem Projekt zu erstellen. Lassen Sie uns sofort eine Django-Anwendung erstellen. Hier erstellen wir eine Anwendung mit dem Namen ** Listen **.
$ python manage.py startapp lists
Unit Tests, and How They Differ from Functional Tests
Funktionstests testen die Anwendung von außen (aus Sicht des Benutzers), um festzustellen, ob sie ordnungsgemäß funktioniert. Unit Tests testen, ob die Anwendung von innen funktioniert (aus Sicht des Entwicklers). TDD ist erforderlich, um Funktionstests und Komponententests abzudecken, und das Entwicklungsverfahren ist wie folgt.
** Schritt 1. ** Schreiben Sie einen Funktionstest (Erläuterung neuer Funktionen aus Sicht des Benutzers).
** Schritt 2. ** Wenn der Funktionstest fehlschlägt, überlegen Sie, wie Sie den Code schreiben, um den Test zu bestehen (schreiben Sie ihn nicht plötzlich). Fügen Sie einen Komponententest hinzu, um zu definieren, wie sich Ihr Code verhalten soll.
** Schritt 3. ** Wenn der Komponententest fehlschlägt, schreiben Sie den Mindestanwendungscode, den der Komponententest bestehen wird.
** Schritt 4. ** Wiederholen Sie Schritt 2 und Schritt 3, um schließlich zu überprüfen, ob der Funktionstest bestanden wurde.
Unit Testing in Django
Ich werde den Test der Ansicht der Homepage in lists / tests.py schreiben. Lassen Sie uns zuerst hier überprüfen.
# lists/tests.py
from django.test import TestCase
# Create your tests here.
Als ich mir das ansah, stellte ich fest, dass ich mit der von Django bereitgestellten TestCase-Klasse einen Komponententest schreiben konnte. djagno.test.TestCase
ist eine Erweiterung des im Funktionstest verwendeten Standardmoduls unittest.TestCase
.
Ich werde einen Unit-Test als Test schreiben.
# lists/tests.py
from django.test import TestCase
class SmokeTest(TestCase):
def test_bad_maths(self):
self.assertEqual(1 + 1, 3)
Django verfügt über eine Testrunner-Funktion, die Tests für jede Anwendung sucht und ausführt. Beginnen wir mit Djangos Testläufer.
$ python manage.py test
Creating test database for alias 'default'...
System check identified no issues (0 silenced).
F
======================================================================
FAIL: test_bad_maths (lists.tests.SmokeTest)
----------------------------------------------------------------------
Traceback (most recent call last):
File "C:\--your_pass--\django-TDD\lists\tests.py", line 9, in test_bad_maths
self.assertEqual(1 + 1, 3)
AssertionError: 2 != 3
----------------------------------------------------------------------
Ran 1 test in 0.005s
FAILED (failures=1)
Destroying test database for alias 'default'...
Ich konnte bestätigen, dass listen / tests.py ausgeführt wurde und fehlgeschlagen ist. Ich werde hier festlegen.
$ git status
$ git add lists
$ git commit -m "Add app for lists, with deliberately failing unit test"
Django's MVC, URLs, and View Functions
Django muss definieren, was mit einer bestimmten URL geschehen soll. Der Django-Workflow sieht folgendermaßen aus:
HTTP / Anfrage kommt zu einer bestimmten URL
Da die Regel festgelegt wurde, welche Ansicht für HTTP / Anforderung ausgeführt werden soll, führen Sie die Ansicht gemäß der Regel aus.
View verarbeitet die Anforderung und gibt HTTP / Antwort zurück.
Daher haben wir zwei Dinge zu tun:
Ist es möglich, die URL zwischen URL und Ansicht aufzulösen?
Kann View den HTML-Code ändern, der den Funktionstest bestehen kann?
Öffnen wir nun lists / tests.py und schreiben einen kleinen Test.
# lists/tests.py
from django.urls import resolve #hinzufügen
from django.test import TestCase
from lists.views import home_page #hinzufügen
class HomePageTest(TestCase):
def test_root_url_resolve_to_home_page_view(self):
found = resolve('/')
self.assertEqual(found.func, home_page)
django.urls.resolve
ist ein Modul zum Auflösen von URLs, die intern von django verwendet werden.
from lists.views import home_page
ist die Ansicht, die als nächstes beschrieben wird. Sie können sehen, dass dies unten beschrieben wird.
Lass es uns testen.
$ python manage.py test
System check identified no issues (0 silenced).
E
======================================================================
ERROR: lists.tests (unittest.loader._FailedTest)
----------------------------------------------------------------------
ImportError: Failed to import test module: lists.tests
Traceback (most recent call last):
File "C:\--your_user_name--\AppData\Local\Programs\Python\Python37\lib\unittest\loader.py", line 436, in _find_test_path
module = self._get_module_from_name(name)
File "C:\--your_user_name--\AppData\Local\Programs\Python\Python37\lib\unittest\loader.py", line 377, in _get_module_from_name
__import__(name)
File "C:\--your_path--\django-TDD\lists\tests.py", line 5, in <module>
from lists.views import home_page
ImportError: cannot import name 'home_page' from 'lists.views' (C:\Users\--your_path--\django-TDD\lists\views.py)
----------------------------------------------------------------------
Ran 1 test in 0.001s
FAILED (errors=1)
ImportError
ist erschienen. Wenn ich mir den Inhalt anschaue, erfahre ich, dass home_page
nicht aus lists.views
importiert werden kann.
Schreiben wir nun home_page
in lists.views
.
# lists/views.py
from django.shortcuts import render
home_page = None
Es klingt wie ein Witz, aber dies sollte den "ImportError" lösen. Denken Sie daran, dass TDD Lust hat, den kleinsten Code zu schreiben, um einen Fehler zu beheben.
Lassen Sie es uns noch einmal testen.
$ python manage.py test
Creating test database for alias 'default'...
System check identified no issues (0 silenced).
E
======================================================================
ERROR: test_root_url_resolve_to_home_page_view (lists.tests.SmokeTest)
----------------------------------------------------------------------
Traceback (most recent call last):
File "C:\--your_path--\django-TDD\lists\tests.py", line 10, in test_root_url_resolve_to_home_page_view
found = resolve('/')
File "C:\--your_path--\django-TDD\venv-tdd\lib\site-packages\django\urls\base.py", line 25, in resolve
return get_resolver(urlconf).resolve(path)
File "C:\--your_path--\django-TDD\venv-tdd\lib\site-packages\django\urls\resolvers.py", line 575, in resolve
raise Resolver404({'tried': tried, 'path': new_path})
django.urls.exceptions.Resolver404: {'tried': [[<URLResolver <URLPattern list> (admin:admin) 'admin/'>]], 'path': ''}
----------------------------------------------------------------------
Ran 1 test in 0.005s
FAILED (errors=1)
Destroying test database for alias 'default'...
Sicher, der "ImportError" wurde behoben, aber der Test schlug erneut fehl. Wenn Sie Traceback aktivieren, können Sie sehen, dass Django einen 404-Fehler zurückgibt, selbst wenn "Auflösen" / "" behoben wird. Dies bedeutet, dass Django "/" nicht auflösen konnte.
urls.py
Django hat urls.py, das URLs Ansichten zuordnet. config / urls.py ist die Haupt-urls.py. Lassen Sie uns hier überprüfen.
# config/urls.py
"""config URL Configuration
The `urlpatterns` list routes URLs to views. For more information please see:
https://docs.djangoproject.com/en/3.0/topics/http/urls/
Examples:
Function views
1. Add an import: from my_app import views
2. Add a URL to urlpatterns: path('', views.home, name='home')
Class-based views
1. Add an import: from other_app.views import Home
2. Add a URL to urlpatterns: path('', Home.as_view(), name='home')
Including another URLconf
1. Import the include() function: from django.urls import include, path
2. Add a URL to urlpatterns: path('blog/', include('blog.urls'))
"""
from django.contrib import admin
from django.urls import path
urlpatterns = [
path('admin/', admin.site.urls),
]
Wenn Sie den Inhalt überprüfen, erfahren Sie, wie Sie eine URL-Zuordnung mit dem Namen "config URL Configuration" schreiben. Lesen Sie sie daher. Dieses Mal fügen wir die Zuordnung zwischen "/" und "home_page" zu "urlpatterns" hinzu, indem wir Funciton-Ansichten schreiben. Außerdem habe ich "admin /" noch nicht verwendet, also kommentiere es aus.
# config/urls.py
from django.contrib import admin
from django.urls import path
from lists import views
urlpatterns = [
# path('admin/', admin.site.urls), #Auskommentieren
path('', views.home_page, name='home')
]
Das Mapping ist abgeschlossen. Mach einen Test.
$ python manage.py test
[...]
TypeError: view must be a callable or a list/tuple in the case of include().
Ich habe die URL-Zuordnung hinzugefügt, damit der "404-Fehler" behoben wurde, aber ich habe einen "TypeError" erhalten. Es scheint, dass dies daran liegt, dass beim Aufrufen von "home_page" aus "lists.view" nichts mit "home_page = None" zurückgegeben wird. Lassen Sie uns dies beheben, indem Sie lists / views.py bearbeiten.
# lists/views.py
from django.shortcuts import render
def home_page():
pass
Ich werde es testen.
$ python manage.py test
Creating test database for alias 'default'...
System check identified no issues (0 silenced).
.
----------------------------------------------------------------------
Ran 1 test in 0.003s
OK
Destroying test database for alias 'default'...
Ich konnte den Unit Test bestehen. Ich werde hier festlegen.
$ git add .
$ git status
$ git commit -m "First unit test and url mapping, dummy view"
Wir werden list / tests.py neu schreiben, damit wir testen können, ob die aktuelle list / views.py tatsächlich HTML zurückgibt.
# lists/tests.py
from django.urls import resolve
from django.test import TestCase
from django.http import HttpRequest
from lists.views import home_page
class HomePageTest(TestCase):
def test_root_url_resolve_to_home_page_view(self):
found = resolve('/')
self.assertEqual(found.func, home_page)
def test_home_page_returns_current_html(self): #hinzufügen
request = HttpRequest()
response = home_page(request)
html = response.content.decode('utf8')
self.assertTrue(html.startswith.('<html>'))
self.assertIn('<title>To-Do lists</title>', html)
self.assertTrue(html.endwith('</html>'))
test_root_url_resolve_to_home_page_view
prüft, ob die URL-Zuordnung korrekt ist.
Ich überprüfe, ob der richtige HTML-Code mit test_home_page_returns_current_html
zurückgegeben werden kann.
Wir haben einen neuen Komponententest hinzugefügt. Testen wir ihn also sofort.
$ python manage.py test
Creating test database for alias 'default'...
System check identified no issues (0 silenced).
E.
======================================================================
ERROR: test_home_page_returns_current_html (lists.tests.SmokeTest)
----------------------------------------------------------------------
Traceback (most recent call last):
File "C:\--your_path--\django-TDD\lists\tests.py", line 18, in test_home_page_returns_current_html
response = home_page(request)
TypeError: home_page() takes 0 positional arguments but 1 was given
----------------------------------------------------------------------
Ran 2 tests in 0.005s
FAILED (errors=1)
Destroying test database for alias 'default'...
Ich habe einen TypeError. Wenn ich den Inhalt überprüfe, heißt es, dass "home_page () 0 Positionsargumente akzeptiert, aber 1 angegeben wurde". Sie können sehen, dass die Definition von "home_page ()" keine Argumente angibt ("0 Positionsargumente"), aber es ist seltsam mit den angegebenen Argumenten ("1 wurde angegeben").
Daher möchte ich listen / views.py neu schreiben.
# lists/views.py
from django.shortcuts import render
def home_page(request): #Veränderung
pass
Das Argument request
wurde zur Funktion home_page ()
hinzugefügt. Lassen Sie uns damit testen.
$ python manage.py test
Creating test database for alias 'default'...
System check identified no issues (0 silenced).
E.
======================================================================
ERROR: test_home_page_returns_current_html (lists.tests.SmokeTest)
----------------------------------------------------------------------
Traceback (most recent call last):
File "C:\--your_path--\django-TDD\lists\tests.py", line 19, in test_home_page_returns_current_html
html = response.content.decode('utf8')
AttributeError: 'NoneType' object has no attribute 'content'
----------------------------------------------------------------------
Ran 2 tests in 0.005s
FAILED (errors=1)
Destroying test database for alias 'default'...
Der TypeError
wurde behoben, aber das nächste Mal bekomme ich einenAttributeError
.
Da "NoneType" -Objekt kein Attribut "content" hat, scheint die Ursache darin zu liegen, dass der Rückgabewert von "home_page (request)" "None" ist.
Ändern Sie list / views.py.
# lists/views.py
from django.shortcuts import render
from django.http import HttpResponse #hinzufügen
def home_page(request):
return HttpResponse()
Es wurde ein Fehler behoben, der "django.http.HttpResponse" zurückgab. Lass es uns testen.
$ python manage.py test
Creating test database for alias 'default'...
System check identified no issues (0 silenced).
F.
======================================================================
FAIL: test_home_page_returns_current_html (lists.tests.SmokeTest)
----------------------------------------------------------------------
Traceback (most recent call last):
File "C:\Users\--your_path--\django-TDD\lists\tests.py", line 20, in test_home_page_returns_current_html
self.assertTrue(html.startswith('<html>'))
AssertionError: False is not true
----------------------------------------------------------------------
Ran 2 tests in 0.005s
FAILED (failures=1)
Destroying test database for alias 'default'...
Der AttributeError
wurde behoben und ein AssertionError
ist aufgetreten. Sie können sehen, dass die Meldung "Falsch ist nicht sicher" angezeigt wird, weil "html.startwith (" ")" "Falsch" ist.
Ändern Sie list / views.py.
# lists/views.py
from django.shortcuts import render
from django.http import HttpResponse
def home_page(request):
return HttpResponse('<html>') #Veränderung
$ python manage.py test
Creating test database for alias 'default'...
System check identified no issues (0 silenced).
F.
======================================================================
FAIL: test_home_page_returns_current_html (lists.tests.SmokeTest)
----------------------------------------------------------------------
Traceback (most recent call last):
File "C:\--your_path--\django-TDD\lists\tests.py", line 21, in test_home_page_returns_current_html
self.assertIn('<title>To-Do lists</title>', html)
AssertionError: '<title>To-Do lists</title>' not found in '<html>'
----------------------------------------------------------------------
Ran 2 tests in 0.005s
FAILED (failures=1)
Destroying test database for alias 'default'...
Es ist auch "AssertionError". Es wird gesagt, dass "
# lists/views.py
from django.shortcuts import render
from django.http import HttpResponse
def home_page(request):
return HttpResponse('<html><title>To-Do lists</title>') #Veränderung
$ python manage.py test
Creating test database for alias 'default'...
System check identified no issues (0 silenced).
F.
======================================================================
FAIL: test_home_page_returns_current_html (lists.tests.SmokeTest)
----------------------------------------------------------------------
Traceback (most recent call last):
File "C:\--your_path--\django-TDD\lists\tests.py", line 22, in test_home_page_returns_current_html
self.assertTrue(html.endswith('</html>'))
AssertionError: False is not true
----------------------------------------------------------------------
Ran 2 tests in 0.013s
FAILED (failures=1)
Destroying test database for alias 'default'...
Es ist auch "AssertionError". Es wird gesagt, dass "</ html>" nicht gefunden werden kann. Ändern Sie list / views.py.
# lists/views.py
from django.shortcuts import render
from django.http import HttpResponse
def home_page(request):
return HttpResponse('<html><title>To-Do lists</title></html>') #Veränderung
Das sollte endlich klappen.
$ python manage.py test
Creating test database for alias 'default'...
System check identified no issues (0 silenced).
..
----------------------------------------------------------------------
Ran 2 tests in 0.004s
OK
Destroying test database for alias 'default'...
Es ging gut. Nachdem der Komponententest bestanden wurde, starten wir den Entwicklungsserver und führen dann den Funktionstest aus.
#Starten Sie den Entwicklungsserver
$ python manage.py runserver
#Starten Sie ein weiteres cmd und führen Sie einen Funktionstest durch
$ python functional_tests.py
DevTools listening on ws://127.0.0.1:51108/devtools/browser/9d1c6c55-8391-491b-9b14-6130c3314bba
F
======================================================================
FAIL: test_can_start_a_list_and_retrieve_it_later (__main__.NewVisitorTest)
----------------------------------------------------------------------
Traceback (most recent call last):
File "functional_tests.py", line 21, in test_can_start_a_list_and_retrieve_it_later
self.fail('Finish the test!')
AssertionError: Finish the test!
----------------------------------------------------------------------
Ran 1 test in 7.244s
FAILED (failures=1)
Der Funktionstest ist fehlgeschlagen, da ich ".fail" in "unittest.TestCase" verwendet habe, um sicherzustellen, dass ein Fehler auftritt, auch wenn der Test erfolgreich ist. Daher können Sie sehen, dass der Funktionstest erfolgreich war!
Lassen Sie uns begehen.
$ git add .
$ git commit -m "Basic view now return minimal HTML"
Stellen Sie sicher, dass Sie es bisher abgedeckt haben.
Die Django-Anwendung wurde gestartet.
Ich habe Djangos Unit Test Runner verwendet.
Ich verstehe den Unterschied zwischen einem Funktionstest und einem Unit-Test.
--Erstellte eine Ansicht mit Djangos Anforderungs- und Antwortobjekten
Ich konnte den Prozess der Funktionserstellung durch Drehen des Komponententests und des Code-Additions- / Korrekturzyklus bestätigen.
Recommended Posts