[PYTHON] Testgetriebene Entwicklung mit Django Teil 6

Testgetriebene Entwicklung mit Django Teil 6

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 ⇒⇒ Klicken Sie hier für Teil 3 - Kapitel 3 ⇒⇒ Klicken Sie hier für Teil 4 - Kapitel 4 ⇒⇒ Klicken Sie hier für Teil 5 - Kapitel 5

Part1. The Basics of TDD and Django

Chapter6. Improving Functional Testss: Ensuring Isolation and Removing Voodoo Sleeps

In Kapitel 5 habe ich überprüft, ob die POST-Daten gespeichert wurden und ob sie problemlos an die Antwort zurückgegeben werden können.

Im Komponententest wird die Test-DB von Django erstellt, sodass die Daten für den Test zum Zeitpunkt der Testausführung gelöscht werden. Im Funktionstest (in der aktuellen Einstellung) wird jedoch die DB für die Produktion (db.sqlite3) verwendet. Es gab ein Problem, dass die Daten zum Zeitpunkt des Tests ebenfalls gespeichert wurden.

Dieses Mal werden wir "Best Practice" für diese Probleme üben.

Ensuring Test Isolation in Functional Tests

Wenn die Testdaten erhalten bleiben, können sie nicht zwischen den Tests getrennt werden, was zu Problemen wie "Der Test, der erfolgreich sein sollte, weil die Testdaten gespeichert werden, schlägt fehl" führt. Es ist wichtig, sich der Trennung zwischen den Tests bewusst zu sein, um dies zu vermeiden.

In Funktionstests können Sie einen Mechanismus implementieren, der automatisch eine Testdatenbank wie einen Komponententest erstellt und diese nach Abschluss des Tests mithilfe der LiveServerTestCase-Klasse in Djagno löscht.

"LiveServerTestCase" soll mit dem Testläufer von Django getestet werden. Wenn der Testläufer von Django ausgeführt wird, werden die Dateien, die mit "test" beginnen, in allen Ordnern ausgeführt.

Erstellen wir daher einen Ordner für Funktionstests wie eine Django-Anwendung.

#Ordner erstellen
$ mkdir functional_tests
#Damit Django es als Python-Paket erkennt
$ type nul > functional_tests/__init__.py
#Bestehende Funktionstests umbenennen und verschieben
$ git mv functional_tests.py functional_tests/tests.py
#Bestätigung
$ git status

Jetzt habe ich einen Funktionstest mit python manage.py function_tests.py durchgeführt Sie können es jetzt mit python manage.py test function_tests ausführen.

Schreiben wir nun den Funktionstest neu.

# django-tdd/functional_tests/tests.py

from django.test import LiveServerTestCase  #hinzufügen
from selenium import webdriver
from selenium.webdriver.common.keys import Keys
import time


class NewVisitorTest(LiveServerTestCase):  #Veränderung

    def setUp(self):
        self.browser = webdriver.Chrome()

    def tearDown(self):
        self.browser.quit()

    def check_for_row_in_list_table(self, row_text):
        table = self.browser.find_element_by_id('id_list_table')
        rows = table.find_elements_by_tag_name('tr')
        self.assertIn(row_text, [row.text for row in rows])

    def test_can_start_a_list_and_retrieve_it_later(self):
        #Nobita ist neu in-Ich habe gehört, dass es eine Do-App gibt und habe auf die Homepage zugegriffen.
        self.browser.get(self.live_server_url)  #Veränderung

        #Nobita hat den Seitentitel und den Header zu-Ich habe bestätigt, dass es sich um eine Do-App handelt.
        self.assertIn('To-Do', self.browser.title)
        header_text = self.browser.find_element_by_tag_name('h1').text
        self.assertIn('To-Do', header_text)

        #Nobita ist zu-Aufforderung zum Ausfüllen des Do-Elements,
        inputbox = self.browser.find_element_by_id('id_new_item')
        self.assertEqual(
            inputbox.get_attribute('placeholder'),
            'Enter a to-do item'
        )

        #Nobita schrieb in das Textfeld "Buy Dorayaki"(Sein bester Freund liebt Dorayaki)
        inputbox.send_keys('Buy dorayaki')

        #Wenn Nobita die Eingabetaste drückt, wird die Seite aktualisiert
        # "1:Dorayaki kaufen"Ist zu-Es stellt sich heraus, dass es als Element zur Aufgabenliste hinzugefügt wurde
        inputbox.send_keys(Keys.ENTER)
        time.sleep(3)  #Warten Sie auf die Seitenaktualisierung.
        self.check_for_row_in_list_table('1: Buy dorayaki')

        #Über das Textfeld können Sie weiterhin Elemente ausfüllen
        #Ausgefüllt "Geld für Dorayaki aufladen"(Er hat wenig Geld)
        inputbox = self.browser.find_element_by_id('id_new_item')
        inputbox.send_keys("Demand payment for the dorayaki")
        inputbox.send_keys(Keys.ENTER)
        time.sleep(3)

        #Die Seite wurde erneut aktualisiert und ich konnte sehen, dass neue Elemente hinzugefügt wurden
        self.check_for_row_in_list_table('2: Demand payment for the dorayaki')

        #Nobita ist das zu-Ich habe mich gefragt, ob die do-App meine Artikel richtig aufzeichnet.
        #Als ich die URL überprüfte, stellte ich fest, dass die URL eine bestimmte URL für Nobita zu sein scheint
        self.fail("Finish the test!")

        #Als Nobita versuchte, auf eine bestimmte URL zuzugreifen, die er einmal bestätigt hatte,

        #Ich war glücklich einzuschlafen, weil die Gegenstände gelagert wurden.

Der Funktionstest wurde geändert, um "LiveServerTestCase" vom "unittest" -Modul zu erben. Jetzt, da Sie Funktionstests mit dem Testläufer von Django ausführen können, habe ich das folgende "if __name ==" __ main __ "entfernt.

Lassen Sie uns nun den Funktionstest durchführen.

$ python manage.py test functional_tests

Creating test database for alias 'default'...
System check identified no issues (0 silenced).

======================================================================
FAIL: test_can_start_a_list_and_retrieve_it_later (functional_tests.tests.NewVisitorTest)
----------------------------------------------------------------------
Traceback (most recent call last):
  File "C:--your_path--\django-TDD\functional_tests\tests.py", line 59, in test_can_start_a_list_and_retrieve_it_later
    self.fail("Finish the test!")
AssertionError: Finish the test!

----------------------------------------------------------------------
Ran 1 test in 28.702s

FAILED (failures=1)
Destroying test database for alias 'default'...

Der Funktionstest endete mit "self.fail" und ergab die gleichen Ergebnisse wie vor dem Anwenden des "LiveServerTestCase". Wir haben außerdem bestätigt, dass eine Datenbank für Funktionstests erstellt und gelöscht wurde, sobald der Test abgeschlossen war.

Lassen Sie uns hier festlegen.

$ git status
$ git add functional_tests
$ git commit -m "make functional_tests an app, use LiveSeverTestCase"

Running Just the Unit Tests

Mit dem Befehl "python manage.py test" kann Django Unit- und Funktionstests zusammen ausführen. Wenn Sie nur den Komponententest testen möchten, geben Sie die Anwendung wie "python manage.py-Testlisten" an.

On Implicit and Explicit Waits, and Voodoo time.sleeps

Ich habe time.sleep (3) hinzugefügt, um das Ergebnis der Ausführung des Funktionstests zu überprüfen. Ob diese "time.sleep (3)" auf 3 Sekunden, 1 Sekunde oder 0,5 Sekunden eingestellt ist, hängt von der Antwort ab, aber ich weiß nicht, was die richtige Antwort ist.

Schreiben wir den Funktionstest neu, sodass nur die erforderlichen Puffer vorbereitet werden. Ändern Sie check_for_row_in_list_table in wait_for_row_in_list_table und fügen Sie eine Polling / Retry-Logik hinzu.

# django-tdd/functional_tests/tests.py

from django.test import LiveServerTestCase
from selenium import webdriver
from selenium.webdriver.common.keys import Keys
from selenium.common.exceptions import WebDriverException  #hinzufügen
import time

MAX_WAIT = 10  #hinzufügen


class NewVisitorTest(LiveServerTestCase):

    def setUp(self):
        self.browser = webdriver.Chrome()

    def tearDown(self):
        self.browser.quit()

    def wait_for_row_in_list_table(self, row_text):
        start_time = time.time()
        while True:
            try:
                table = self.browser.find_element_by_id('id_list_table')
                rows = table.find_elements_by_tag_name('tr')
                self.assertIn(row_text, [row.text for row in rows])
                return
            except (AssertionError, WebDriverException) as e:
                if time.time() - start_time > MAX_WAIT:
                    raise e
                time.sleep(0.5)
    [...]

Dadurch können wir die Verarbeitung nur der Puffer beenden, die wir für die Antwort benötigen (wir werden sie später umgestalten). Ich versuche bis zu 10 Sekunden zu warten.

Lassen Sie uns den Teil, der check_for_row_in_list_table ausführte, in wait_for_row_in_list_table ändern undtime.sleep (3)löschen. Infolgedessen sieht der aktuelle Funktionstest folgendermaßen aus:

# django-tdd/functional_tests/tests.py

from django.test import LiveServerTestCase
from selenium import webdriver
from selenium.webdriver.common.keys import Keys
from selenium.common.exceptions import WebDriverException  #hinzufügen
import time

MAX_WAIT = 10  #hinzufügen


class NewVisitorTest(LiveServerTestCase):  #Veränderung

    def setUp(self):
        self.browser = webdriver.Chrome()

    def tearDown(self):
        self.browser.quit()

    def wait_for_row_in_list_table(self, row_text):
        start_time = time.time()
        while True:
            try:
                table = self.browser.find_element_by_id('id_list_table')
                rows = table.find_elements_by_tag_name('tr')
                self.assertIn(row_text, [row.text for row in rows])
                return
            except (AssertionError, WebDriverException) as e:
                if time.time() - start_time > MAX_WAIT:
                    raise e
                time.sleep(0.5)

    def test_can_start_a_list_and_retrieve_it_later(self):
        #Nobita ist neu in-Ich habe gehört, dass es eine Do-App gibt und habe auf die Homepage zugegriffen.
        self.browser.get(self.live_server_url)  #Veränderung

        #Nobita hat den Seitentitel und den Header zu-Ich habe bestätigt, dass es sich um eine Do-App handelt.
        self.assertIn('To-Do', self.browser.title)
        header_text = self.browser.find_element_by_tag_name('h1').text
        self.assertIn('To-Do', header_text)

        #Nobita ist zu-Aufforderung zum Ausfüllen des Do-Elements,
        inputbox = self.browser.find_element_by_id('id_new_item')
        self.assertEqual(
            inputbox.get_attribute('placeholder'),
            'Enter a to-do item'
        )

        #Nobita schrieb in das Textfeld "Buy Dorayaki"(Sein bester Freund liebt Dorayaki)
        inputbox.send_keys('Buy dorayaki')

        #Wenn Nobita die Eingabetaste drückt, wird die Seite aktualisiert
        # "1:Dorayaki kaufen"Ist zu-Es stellt sich heraus, dass es als Element zur Aufgabenliste hinzugefügt wurde
        inputbox.send_keys(Keys.ENTER)
        self.wait_for_row_in_list_table('1: Buy dorayaki')

        #Über das Textfeld können Sie weiterhin Elemente ausfüllen
        #Ausgefüllt "Geld für Dorayaki aufladen"(Er hat wenig Geld)
        inputbox = self.browser.find_element_by_id('id_new_item')
        inputbox.send_keys("Demand payment for the dorayaki")
        inputbox.send_keys(Keys.ENTER)

        #Die Seite wurde erneut aktualisiert und ich konnte sehen, dass neue Elemente hinzugefügt wurden
        self.wait_for_row_in_list_table('2: Demand payment for the dorayaki')

        #Nobita ist das zu-Ich habe mich gefragt, ob die do-App meine Artikel richtig aufzeichnet.
        #Als ich die URL überprüfte, stellte ich fest, dass die URL eine bestimmte URL für Nobita zu sein scheint
        self.fail("Finish the test!")

        #Als Nobita versuchte, auf eine bestimmte URL zuzugreifen, die er einmal bestätigt hatte,

        #Ich war glücklich einzuschlafen, weil die Gegenstände gelagert wurden.

Als ich den Funktionstest ausführte, endete er mit "self.fail (" Test beenden! "). AssertionError: Beenden Sie den Test!".

Testing "Best practives" applied in this chapter

Hier finden Sie eine Zusammenfassung der Best Practices in Kapitel 6.

Recommended Posts

Testgetriebene Entwicklung mit Django Teil 3
Testgetriebene Entwicklung mit Django Teil 4
Testgetriebene Entwicklung mit Django Teil 6
Testgetriebene Entwicklung mit Django Teil 2
Testgetriebene Entwicklung mit Django Teil 1
Testgetriebene Entwicklung mit Django Teil 5
Testgetriebenes Entwicklungsstart mit PySide & Pytest
Entwicklungsverdauung mit Django
Django-Test
[Test Driven Development (TDD)] Kapitel 21 Zusammenfassung
App-Entwicklung mit SQLite mit Django (PTVS)
Django startete Teil 1
Erleben Sie Teil I "Multinationale Währung" des Buches "Test Driven Development" mit Python
Internationalisierung mit Django
Django startete Teil 4
CRUD mit Django
Erste Django-Entwicklung
Erstellen Sie solche Testdaten mit Python (Teil 1)
[Python] Erstellen Sie mit Docker eine Django-Entwicklungsumgebung
Django Erste Schritte Teil 2 mit dem Eclipse Plugin (PyDev)
Erstellen Sie mit der Doker Toolbox eine Django-Entwicklungsumgebung
Authentifizieren Sie Google mit Django
Django 1.11 wurde mit Python3.6 gestartet
Laden Sie Dateien mit Django hoch
Mit Codetest stärken ⑦
PDF mit Django ausgeben
Mit Codetest stärken ⑨
Markdown-Ausgabe mit Django
Verwenden Sie Gentelella mit Django
Twitter OAuth mit Django
Mit Codetest stärken ⑤
Mit Codetest stärken ④
Erste Schritte mit Django 1
Primzahlbeurteilung mit Python
Mail mit Django senden
Sandkasten mit neo4j Teil 10
Webanwendung erstellt mit Python3.4 + Django (Teil.1 Umgebungskonstruktion)
Datei-Upload mit Django
Mit Codetest stärken ②
Verwenden Sie WENIGER mit Django
Pooling mechanisieren mit Django
Verwenden Sie MySQL mit Django
Mit Codetest stärken ①
Erstellen Sie eine Entwicklungsumgebung mit Poetry Django Docker Pycharm
[Memo] Django-Entwicklungsumgebung
Artikel, die die Systementwicklung mit Django (Python) ermöglichen _Einführung
Django ab heute
Erste Schritte mit Django 2
Mit Codetest stärken ⑧
Mit Codetest stärken ⑨
Tutorial für die testgetriebene Entwicklung (TDD) mit Flask-2-Dekorateuren
Erstellen Sie mit Docker eine Django-Entwicklungsumgebung! (Docker-compose / Django / postgreSQL / nginx)
[Memo] Erstellen Sie mit Docker eine Entwicklungsumgebung für Django + Nuxt.js
[Django] Erstellen Sie mit PyCharm schnell eine Entwicklungsumgebung für Django-Container (Docker)
Erstellen Sie mit Django eine Bulletin-Board-App von Grund auf neu. (Teil 2)
Erstellen Sie mit Django eine Bulletin-Board-App von Grund auf neu. (Teil 3)