[PYTHON] Développement piloté par les tests avec Django Partie 6

Développement piloté par les tests avec Django Partie 6

Ceci est une note d'apprentissage pour comprendre le développement piloté par les tests (TDD) dans Django.

Les références sont [** Test-Driven Development with Python: Obey the Testing Goat: Using Django, Selenium, and JavaScript (English Edition) 2nd Edition **](https://www.amazon.co.jp/dp/B074HXXXLS Nous allons procéder à l'apprentissage basé sur / ref = dp-kindle-redirect? _ Encoding = UTF8 & btkr = 1).

Dans ce livre, nous menons des tests fonctionnels en utilisant la série Django 1.1 et FireFox, mais cette fois nous effectuerons des tests fonctionnels sur la série Djagno 3 et Google Chrome. J'ai également apporté quelques modifications personnelles (comme changer le nom du projet en Config), mais il n'y a pas de changements majeurs.

⇒⇒ Cliquez ici pour Partie 1 - Chapitre 1 ⇒⇒ Cliquez ici pour Partie 2-Chapitre 2 ⇒⇒ Cliquez ici pour Partie 3-Chapitre3 ⇒⇒ Cliquez ici pour Partie 4-Chapitre 4 ⇒⇒ Cliquez ici pour Partie 5 - Chapitre 5

Part1. The Basics of TDD and Django

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

Au chapitre 5, j'ai vérifié si les données POSTées étaient sauvegardées et si elles pouvaient être renvoyées à la réponse sans aucun problème.

Dans le test unitaire, la base de données de test de Django est créée, donc les données pour le test sont supprimées au moment de l'exécution du test, mais dans le test fonctionnel (dans le paramètre actuel), la base de données pour la production (db.sqlite3) est utilisée. Un problème est survenu lorsque les données au moment du test ont également été enregistrées.

Cette fois, nous pratiquerons les «meilleures pratiques» pour ces problèmes.

Ensuring Test Isolation in Functional Tests

Si les données de test restent, elles ne peuvent pas être séparées entre les tests, ce qui provoque des problèmes tels que «le test qui doit réussir car les données de test sont enregistrées échoue». Il est important d'être conscient de la séparation entre les tests pour éviter cela.

Dans les tests fonctionnels, vous pouvez implémenter un mécanisme qui crée automatiquement une base de données de test comme un test unitaire et la supprime une fois le test terminé en utilisant la classe LiveServerTestCase dans Djagno.

LiveServerTestCase est destiné à être testé en utilisant le testeur de Django. Lorsque le test runner de Django s'exécute, il exécute les fichiers commençant par test dans tous les dossiers.

Par conséquent, créons un dossier pour les tests fonctionnels comme une application Django.

#Créer le dossier
$ mkdir functional_tests
#Pour que Django le reconnaisse comme un package Python
$ type nul > functional_tests/__init__.py
#Renommer et déplacer les tests fonctionnels existants
$ git mv functional_tests.py functional_tests/tests.py
#Vérification
$ git status

J'exécutais maintenant un test fonctionnel avec python manage.py functional_tests.py Vous pouvez maintenant l'exécuter avec python manage.py test functional_tests.

Réécrivons maintenant le test fonctionnel.

# django-tdd/functional_tests/tests.py

from django.test import LiveServerTestCase  #ajouter à
from selenium import webdriver
from selenium.webdriver.common.keys import Keys
import time


class NewVisitorTest(LiveServerTestCase):  #Changement

    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 est une nouveauté-J'ai entendu dire qu'il y avait une application do et j'ai accédé à la page d'accueil.
        self.browser.get(self.live_server_url)  #Changement

        #Nobita a le titre et l'en-tête de la page-J'ai confirmé que cela suggère qu'il s'agit d'une application do.
        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 est de-Invité à remplir l'élément à faire
        inputbox = self.browser.find_element_by_id('id_new_item')
        self.assertEqual(
            inputbox.get_attribute('placeholder'),
            'Enter a to-do item'
        )

        #Nobita a écrit dans la zone de texte "Acheter Dorayaki"(Son meilleur ami aime dorayaki)
        inputbox.send_keys('Buy dorayaki')

        #Lorsque Nobita appuie sur Entrée, la page est actualisée
        # "1:Acheter Dorayaki"Est de-Il s'avère qu'il a été ajouté en tant qu'élément à la liste de tâches
        inputbox.send_keys(Keys.ENTER)
        time.sleep(3)  #Attendez l'actualisation de la page.
        self.check_for_row_in_list_table('1: Buy dorayaki')

        #La zone de texte vous permet de continuer à remplir les éléments, donc
        #Rempli "Facturation de l'argent pour Dorayaki"(Il est à court d'argent)
        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)

        #La page a été actualisée à nouveau et j'ai pu voir que de nouveaux éléments ont été ajoutés
        self.check_for_row_in_list_table('2: Demand payment for the dorayaki')

        #Nobita est-ce pour-Je me demandais si l'application do enregistrait correctement mes éléments,
        #Lorsque j'ai vérifié l'URL, j'ai trouvé que l'URL semble être une URL spécifique pour Nobita
        self.fail("Finish the test!")

        #Lorsque Nobita a tenté d'accéder à une URL spécifique qu'il avait confirmée une fois,

        #J'étais heureux de m'endormir car les articles étaient rangés.

Modification du test fonctionnel pour hériter de LiveServerTestCase du module ʻunittest. Maintenant que vous pouvez exécuter des tests fonctionnels en utilisant le lanceur de tests de Django, j'ai supprimé ce qui suit: ʻif __name == '__ main __'

Maintenant, exécutons le test fonctionnel.

$ 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'...

Le test fonctionnel s'est terminé par «self.fail» et a donné les mêmes résultats qu'avant l'application de «LiveServerTestCase». Nous avons également confirmé qu'une base de données pour les tests fonctionnels avait été créée et supprimée dès que le test était terminé.

Engageons-nous ici.

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

Running Just the Unit Tests

La commande python manage.py test permet à Django d'exécuter des tests unitaires et fonctionnels ensemble. Si vous souhaitez tester uniquement le test unitaire, spécifiez l'application comme python manage.py test lists.

On Implicit and Explicit Waits, and Voodoo time.sleeps

J'ai ajouté time.sleep (3) pour vérifier le résultat de l'exécution du test fonctionnel. Que ce "time.sleep (3)" soit réglé sur 3 secondes, 1 seconde ou 0,5 seconde dépend de la réponse, mais je ne sais pas quelle est la bonne réponse.

Réécrivons le test fonctionnel pour que seuls les tampons nécessaires soient préparés. Remplacez check_for_row_in_list_table par wait_for_row_in_list_table et ajoutez une logique d'interrogation / nouvelle tentative.

# 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  #ajouter à
import time

MAX_WAIT = 10  #ajouter à


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)
    [...]

Cela nous permet d'arrêter de traiter uniquement les tampons dont nous avons besoin pour la réponse (nous la refactoriserons plus tard). J'essaye d'attendre jusqu'à 10 secondes.

Changeons la partie qui exécutait check_for_row_in_list_table en wait_for_row_in_list_table et supprimonstime.sleep (3). En conséquence, le test fonctionnel actuel ressemble à ceci:

# 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  #ajouter à
import time

MAX_WAIT = 10  #ajouter à


class NewVisitorTest(LiveServerTestCase):  #Changement

    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 est une nouveauté-J'ai entendu dire qu'il y avait une application do et j'ai accédé à la page d'accueil.
        self.browser.get(self.live_server_url)  #Changement

        #Nobita a le titre et l'en-tête de la page-J'ai confirmé que cela suggère qu'il s'agit d'une application do.
        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 est de-Invité à remplir l'élément à faire
        inputbox = self.browser.find_element_by_id('id_new_item')
        self.assertEqual(
            inputbox.get_attribute('placeholder'),
            'Enter a to-do item'
        )

        #Nobita a écrit dans la zone de texte "Acheter Dorayaki"(Son meilleur ami aime dorayaki)
        inputbox.send_keys('Buy dorayaki')

        #Lorsque Nobita appuie sur Entrée, la page est actualisée
        # "1:Acheter Dorayaki"Est de-Il s'avère qu'il a été ajouté en tant qu'élément à la liste de tâches
        inputbox.send_keys(Keys.ENTER)
        self.wait_for_row_in_list_table('1: Buy dorayaki')

        #La zone de texte vous permet de continuer à remplir les éléments, donc
        #Rempli "Facturation de l'argent pour Dorayaki"(Il est à court d'argent)
        inputbox = self.browser.find_element_by_id('id_new_item')
        inputbox.send_keys("Demand payment for the dorayaki")
        inputbox.send_keys(Keys.ENTER)

        #La page a été actualisée à nouveau et j'ai pu voir que de nouveaux éléments ont été ajoutés
        self.wait_for_row_in_list_table('2: Demand payment for the dorayaki')

        #Nobita est-ce pour-Je me demandais si l'application do enregistrait correctement mes éléments,
        #Lorsque j'ai vérifié l'URL, j'ai trouvé que l'URL semble être une URL spécifique pour Nobita
        self.fail("Finish the test!")

        #Lorsque Nobita a tenté d'accéder à une URL spécifique qu'il avait confirmée une fois,

        #J'étais heureux de m'endormir car les articles étaient rangés.

Quand j'ai exécuté le test fonctionnel, il s'est terminé par self.fail (" Terminez le test! ") AssertionError: Terminez le test!.

Testing "Best practives" applied in this chapter

Voici un résumé des meilleures pratiques au chapitre 6.

Recommended Posts

Développement piloté par les tests avec Django Partie 3
Développement piloté par les tests avec Django Partie 4
Développement piloté par les tests avec Django Partie 6
Développement piloté par les tests avec Django Partie 2
Développement piloté par les tests avec Django Partie 1
Développement piloté par les tests avec Django Partie 5
Démarrage du développement piloté par les tests avec PySide & Pytest
Résumé du développement avec Django
Test Django
[Test Driven Development (TDD)] Chapitre 21 Résumé
Développement d'applications à l'aide de SQLite avec Django (PTVS)
Django a commencé la partie 1
Découvrez la partie I «Monnaie multinationale» du livre «Test Driven Development» avec Python
Internationalisation avec Django
Django a commencé la partie 4
CRUD avec Django
Premier développement Django
Créez des données de test comme ça avec Python (partie 1)
[Python] Créer un environnement de développement Django avec Docker
Django Getting Started Part 2 avec eclipse Plugin (PyDev)
Créer un environnement de développement Django à l'aide de Doker Toolbox
Authentifier Google avec Django
Django 1.11 a démarré avec Python3.6
Télécharger des fichiers avec Django
Renforcez avec le test de code ⑦
Sortie PDF avec Django
Renforcez avec le test de code ⑨
Renforcez avec le test de code ③
Sortie Markdown avec Django
Utiliser Gentelella avec Django
Twitter OAuth avec Django
Renforcez avec le test de code ⑤
Renforcez avec le test de code ④
Premiers pas avec Django 1
Jugement des nombres premiers avec python
Envoyer des e-mails avec Django
bac à sable avec neo4j partie 10
Application Web réalisée avec Python3.4 + Django (Construction de l'environnement Part.1)
Téléchargement de fichiers avec django
Renforcez avec le test de code ②
Utilisez LESS avec Django
La mutualisation mécanise avec Django
Utiliser MySQL avec Django
Renforcez avec le test de code ①
Créez un environnement de développement avec Poetry Django Docker Pycharm
[Memo] Environnement de développement Django
Articles permettant le développement de systèmes avec Django (Python) _Introduction
Django à partir d'aujourd'hui
Premiers pas avec Django 2
Renforcez avec le test de code ⑧
Renforcez avec le test de code ⑨
Tutoriel pour faire du développement piloté par les tests (TDD) avec Flask-2 Decorators
Créez un environnement de développement Django avec Docker! (Docker-compose / Django / postgreSQL / nginx)
[Memo] Construire un environnement de développement pour Django + Nuxt.js avec Docker
[Django] Créez rapidement un environnement de développement de conteneur Django (Docker) avec PyCharm
Créez une application de tableau d'affichage à partir de zéro avec Django. (Partie 2)
Créez une application de tableau d'affichage à partir de zéro avec Django. (Partie 3)