Cette fois, je vais aborder les tests en dehors des fonctions de l'application.
J'avais l'habitude de faire ce genre de chose pour obtenir un emploi, mais quand je l'ai rendu public et que le responsable le voit lors de l'entretien, ou de la personne qui l'a touché, le cadre De nombreuses personnes ont souligné qu'elles ne comprenaient pas suffisamment. L'un d'eux était qu'il n'y avait pas de code de test.
Extrait du tutoriel officiel
Pourquoi vous devez créer un test Pourquoi faire un test? Et pourquoi maintenant? Peut-être êtes-vous trop occupé à apprendre Python ou Django, et apprendre quelque chose d'autre peut sembler intimidant et inutile. C'est parce que l'application de vote fonctionne bien et que l'introduction de tests automatisés n'améliore pas l'application. Si le seul but de l'apprentissage de la programmation Django est de créer cette application de vote, je suis sûr que vous n'avez pas besoin d'implémenter des tests automatisés. Mais sinon, c'est le moment idéal pour en savoir plus sur les tests automatisés.
Le tutoriel officiel présente également le test de cette manière. Alors pourquoi écrire un test
Si vous automatisez et incorporez la vérification d'algorithme, si vous modifiez l'un des éléments qui composent le système, cela ne fonctionnera pas correctement, ou sinon, quel sera le problème? Vous pouvez immédiatement saisir si oui ou non.
À l'inverse, être capable d'écrire du code qui vérifie un algorithme prouve que vous avez une bonne compréhension de cet algorithme.
J'ai senti qu'il était grossièrement divisé en ces deux points. Le premier est important du point de vue de ce que l'on appelle l'exhaustivité et la maintenance. Surtout si vous êtes un débutant comme nous
Extrait du tutoriel officiel
La rédaction de tests peut vous aider à collaborer en équipe. Jusqu'à présent, j'ai écrit du point de vue d'un développeur qui gère l'application. Cependant, les applications complexes deviennent gérées par l'équipe. Les tests vous protègent contre la rupture accidentelle du code que vous écrivez (et également contre la rupture du code écrit par d'autres). Si vous envisagez de vivre en tant que programmeur Django, vous devez absolument écrire de bons tests!
J'ai tendance à manquer de ce genre de réflexion, mais même si je le fais moi-même, je le ferai en équipe parce que le moi d'hier, ma faute de frappe il y a quelques heures et un code étrange gêneront le code que j'ai écrit maintenant. Quand il s'agit de cela, il n'est pas difficile d'imaginer qu'il y a un risque.
Concernant ce dernier, j'ai senti que ce n'était qu'après le test que je pouvais m'appeler codeur. Allons-nous en.
Opinion d'écrire un test puis d'écrire du code → Il est basé sur la théorie selon laquelle si vous écrivez un test, vous pouvez écrire du code. Clarifiez le problème avec le processus que vous souhaitez réaliser avant de commencer à écrire du code.
Opinion pour écrire du code puis tester → Écrivez selon la théorie de vérifier si le code que vous avez écrit fonctionne normalement.
Ajoutez de nouvelles fonctionnalités, vérifiez les bogues et modifiez les composants existants.
Je ne sais pas quelle est la bonne réponse, mais je pense que la personne qui peut faire 1 est déjà un bon programmeur. Ensuite, je pense que ce que nous recherchons les débutants, c'est d'être conscients d'aller à la troisième étape, qui est un compromis entre 1 et 2.
Prendre le temps d'écrire un test est un bon détour du point de vue de simplement «faire quelque chose qui bouge», et c'est un endroit où les débutants se sentent mal.
Cependant, comme je l'ai dit à plusieurs reprises, si vous ne pouvez pas écrire un test, vous ne pouvez pas vous empêcher d'être évalué par d'autres personnes qui ne comprennent pas le code que vous avez écrit, donc la première chose à faire pour en sortir. Je pense que cela correspond à 3.
Maintenant, faisons les éléments de test du tutoriel Django tout en gardant à l'esprit ce que nous avons écrit dans 3.
Rappelez-vous qu'il y avait un bogue dans models.py
dans précédemment?
C'est un bogue que la soi-disant ** date future ** est affichée.
Exécutez la commande suivante à l'aide de la commande shell.
import datetime
from django.utils import timezone
from polls.models import Question
future_question = Question(pub_date=timezone.now() + datetime.timedelta(days=30)) #1
future_question.was_published_recently() #2
Dans 1, la méthode timedelta
est utilisée pour créer une instance d'une date future.
Puisque pub_date = timezone.now () + datetime.timedelta (days = 30)
, nous publierons une instance avec des informations de date 30 jours après la date actuelle.
En 2, la méthode was_published_recently ()
est exécutée en utilisant cette instance.
Qu'est-ce que tu fais
return self.pub_date >= timezone.now() - datetime.timedelta(days=1)
C'était une méthode qui renvoie «True» si les informations de date de l'instance ne sont pas dans le passé, sinon elle renvoie «False». Cependant, il a été dit qu'il y a un bogue selon lequel ce sera "True" même s'il s'agit d'une date future. Donc, le résultat de 2 est également "Vrai". Créons maintenant un test pour trouver ce bogue.
polls/tests.py
import datetime
from django.test import TestCase
from django.utils import timezone
from .models import Question
class QuestionModelTests(TestCase):
def test_was_published_recently_with_future_question(self):
time = timezone.now() + datetime.timedelta(days=30)
future_question = Question(pub_date=time)
self.assertIs(future_question.was_published_recently(), False)
Le fichier qui décrit les tests dans Django est tests.py.
Importons l'objet TestCase
au début.
La façon d'écrire un test est de créer une classe qui hérite de django.test.TestCase
et d'y écrire la méthode.
D'ailleurs, en même temps que l'héritage de «TestCase», il hérite également de «TransactionTestCase» et «SimpleTestCase».
Pour les applications qui n'utilisent pas de base de données, il semble préférable d'hériter uniquement de «SimpleTestCase».
Donc, pour faire simple, ce que fait ce TestCase
, c'est que lorsque tests.py est exécuté, il commence à rechercher des classes qui héritent de TestCase.
Et quand tu le découvre
Est effectuée. En d'autres termes, on peut dire que le test et chaque méthode du test sont quelque peu indépendants du point de vue de l'application. Référence 1 Référence 2
Ensuite, que fait la méthode test_was_published_recently_with_future_question
?
vérifie que ʻa = b
, c'est-à-dire quefuture_question.was_published_recently ()
est False
.Cela signifie que. Maintenant que vous comprenez cela, exécutons le test.
py manage.py test polls
Si vous avez plusieurs applications, remplacez les sondages par n'importe quel nom de dossier d'application. Comme résultat d'exécution
System check identified no issues (0 silenced).
F
======================================================================
FAIL: test_was_published_recently_with_future_question (polls.tests.QuestionModelTests)
----------------------------------------------------------------------
Traceback (most recent call last):
File "/path/to/mysite/polls/tests.py", line 16, in test_was_published_recently_with_future_question
self.assertIs(future_question.was_published_recently(), False)
AssertionError: True is not False
----------------------------------------------------------------------
Ran 1 test in 0.001s
FAILED (failures=1)
Destroying test database for alias 'default'...
Vous devriez voir quelque chose comme ça.
Vous pouvez voir ici que was_published_recently ()
renvoie True pour une date future, car nous obtenons l'erreur ʻAssertionError: True is not False`.
Vous avez trouvé un bug.
Maintenant que nous avons trouvé un bogue, nous allons écrire du code pour le corriger.
# return self.pub_date >= timezone.now() - datetime.timedelta(days=1)Vers le suivant
def was_published_recently(self):
now = timezone.now()
return now - datetime.timedelta(days=1) <= self.pub_date <= now
Si vous souhaitez renvoyer «True» à une date ultérieure, vous pouvez ajouter la date actuelle à la spécification de condition et la définir sur la valeur maximale.
Au fait, quand il s'agit de tests rigoureux
polls/tests.py
def test_was_published_recently_with_old_question(self):
"""
was_published_recently() returns False for questions whose pub_date
is older than 1 day.
"""
time = timezone.now() - datetime.timedelta(days=1, seconds=1)
old_question = Question(pub_date=time)
self.assertIs(old_question.was_published_recently(), False)
def test_was_published_recently_with_recent_question(self):
"""
was_published_recently() returns True for questions whose pub_date
is within the last day.
"""
time = timezone.now() - datetime.timedelta(hours=23, minutes=59, seconds=59)
recent_question = Question(pub_date=time)
self.assertIs(recent_question.was_published_recently(), True)
Définissez les deux nouvelles méthodes ci-dessus dans tests.py.
La méthode test_was_published_recently_with_old_question ()
est une méthode qui vérifie quewas_published_recently ()
a renvoyé True à la date passée.
La méthode test_was_published_recently_with_recent_question ()
est une méthode qui vérifie quewas_published_recently ()
renvoie True
à la date actuelle.
Maintenant que nous avons testé l'algorithme, vous vous demanderez s'il fonctionne réellement dans votre application.
Donc, cette fois, j'écrirai un test pour View
.
Tout d'abord, ajoutez les modules et classes de test suivants à la classe que vous avez créée précédemment.
pols/tests.py
from django.urls import reverse
#Omission
def create_question(question_text, days):
time = timezone.now() + datetime.timedelta(days=days)
return Question.objects.create(question_text=question_text, pub_date=time)
class QuestionIndexViewTests(TestCase):
def test_no_questions(self):
response = self.client.get(reverse('polls:index'))
self.assertEqual(response.status_code, 200)
self.assertContains(response, "No polls are available.")
self.assertQuerysetEqual(response.context['latest_question_list'], [])
def test_past_question(self):
#Créer une instance de question avec une date passée
create_question(question_text="Past question.", days=-30)
response = self.client.get(reverse('polls:index'))
self.assertQuerysetEqual(
response.context['latest_question_list'],
['<Question: Past question.>']
)
def test_future_question(self):
create_question(question_text="Future question.", days=30)
response = self.client.get(reverse('polls:index'))
self.assertContains(response, "No polls are available.")
self.assertQuerysetEqual(response.context['latest_question_list'], [])
def test_future_question_and_past_question(self):
create_question(question_text="Past question.", days=-30)
create_question(question_text="Future question.", days=30)
response = self.client.get(reverse('polls:index'))
self.assertQuerysetEqual(
response.context['latest_question_list'],
['<Question: Past question.>']
)
def test_two_past_questions(self):
create_question(question_text="Past question 1.", days=-30)
create_question(question_text="Past question 2.", days=-5)
response = self.client.get(reverse('polls:index'))
self.assertQuerysetEqual(
response.context['latest_question_list'],
['<Question: Past question 2.>', '<Question: Past question 1.>']
)
La méthode create_question
est une méthode pour créer un objet Question
qui prend question_text
et jours
comme arguments.
Puisque l'objet Question
sera créé dans les classes de test suivantes, définissez-le au début pour ne pas avoir à écrire le code à chaque fois.
Jetons un coup d'œil à la classe QuestionIndexViewTests
.
La classe client
qui apparaît tout au long de l'histoire est une classe utilisée pour les tests et est une classe qui simule l'accès dans le navigateur.
Référence
Par exemple, écrire «self.client.get» testera le traitement réel de la demande comme «self.objects.get».
test_no_questions
est une méthode pour vérifier le comportement lorsque l'objet Question
n'existe pas.
Cliquez maintenant sur l'URL avec response = self.client.get (reverse ('polls: index'))
.
self.assertEqual(response.status_code, 200)
self.assertContains(response, "No polls are available.")
self.assertQuerysetEqual(response.context['latest_question_list'], [])
Les trois ci-dessus vérifient les arguments de la même manière que la méthode ʻassertIs () mentionnée précédemment. Par exemple,
self.assertEqual (response.status_code, 200)vérifiera si la page est affichée dans le navigateur après avoir frappé l'URL plus tôt. Puisque le code d'accès de 200 est émis lorsque la page est affichée normalement, nous vérifions
response.status_code == 200. En passant, lors de la vérification du fonctionnement de POST tel que formulaire, ce sera
self.client.post`.
self.assertContains ()
vérifie si l'argument contient celui spécifié dans le deuxième argument.
Par exemple, dans ce cas, context
est inclus dans la valeur de retour de self.client.get (reverse ('polls: index')) ʻin
response`, donc la chaîne de caractères spécifiée. Il sera vérifié s'il y en a.
self.assertQuerysetEqual ()
vérifie s'il y a des données dans le jeu de requêtes.
À propos, l'ensemble de requêtes fait référence au type de données comme «int», et plus grosso modo, pensez-y comme une série d'informations extraites du modèle.
Cette fois, nous avons frappé l'URL du chemin de polls: index
, donc la classe ʻIndexView sera exécutée. Ensuite, dans la classe ʻIndexView
, les données sont finalement extraites de la base de données par la méthodeget_queryset ()
, donc l'ensemble de requêtes y est né.
Au fait
Puisque self.assertQuerysetEqual (response.context ['latest_question_list'], [])
, dans ce cas, cela signifie que latest_question_list
est vide, c'est-à-dire qu'il vérifie qu'il n'y a pas de questions.
Il en va de même pour les méthodes d'essai suivantes.
Créez un objet Question
en spécifiant les valeurs de question_text
et days
comme arguments de la méthodecreate_question ()
définie et testez en fonction de celle-ci.
Concernant le résultat de l'exécution de response.context ()
, dans le modèle
<li><a href="{% url 'polls:detail' question.id %}">{{ question.question_text }}</a></li>
Depuis que j'ai fait une telle description, j'ai spécifié question_text
comme argument, donc je me souviens que celui qui y est spécifié est entré dans {{question.question_text}}
.
Ainsi, par exemple, la méthode test_past_question ()
placera ici la `question précédente.
Donc
self.assertQuerysetEqual(response.context['latest_question_list'],['<Question: Past question.>'])
Il est important de comprendre qu'une telle déclaration signifie que la valeur de retour vérifie que le contexte html à l'intérieur de la «réponse» contient la «question passée».
Si vous comprenez jusqu'ici, dans test_future_question ()
self.assertQuerysetEqual(response.context['latest_question_list'], [])
Je ne pense pas que ce soit difficile parce que vous pouvez voir que c'est vrai.
Enfin, modifiez View
avant de tester.
J'ai corrigé le bug du modèle, mais c'est parce qu'il se retrouverait avec un objet avec une date future.
Importez l'objet timezone
du module django.utils
et modifiez la classe ʻIndexView` comme suit:
from django.utils import timezone
class IndexView(generic.ListView):
template_name = 'polls/index.html'
context_object_name = 'latest_question_list'
def get_queryset(self):
return Question.objects.filter(
pub_date__lte = timezone.now()
).order_by('-pub_date')[:5]
Pensez à pub_date__lte = timezone.now ()
comme synonyme depub_date <= timezone.now ()
.
En d'autres termes, le processus renvoie un ensemble de requêtes qui récupère 5 objets «Question» avec des informations de date antérieures à la date actuelle.
Bien qu'il soit correct de corriger jusqu'à ʻIndex, les données de la date future seront toujours affichées dans
DetailView` telles quelles, donc corrigeons-les également ici.
Modifiez comme suit.
polls/views.py
class DetailView(generic.DetailView):
...
def get_queryset(self):
return Question.objects.filter(pub_date__lte=timezone.now())
Ensuite, ajoutez la classe de test suivante à tests.py
.
class QuestionDetailViewTests(TestCase):
def test_future_question(self):
future_question = create_question(question_text='Future question.', days=5)
url = reverse('polls:detail', args=(future_question.id,))
response = self.client.get(url)
self.assertEqual(response.status_code, 404)
def test_past_question(self):
past_question = create_question(question_text='Past Question.', days=-5)
url = reverse('polls:detail', args=(past_question.id,))
response = self.client.get(url)
self.assertContains(response, past_question.question_text
Fondamentalement, la théorie est la même que le test précédent. La différence est le test DetailView, alors préparez la partie 34
de l'URL telle que / polls / 34
à l'avance sous la forme ʻurl = reverse ('polls: detail', args = (future_question.id,))`. Vous devez le spécifier en tant qu'URL distincte.
Au fait, certains d'entre vous l'ont peut-être remarqué en écrivant des tests jusqu'à présent, mais par exemple, dans le cas de l'application réalisée dans ce tutoriel, vous devez écrire un test pour resluts.html
, c'est-à-dire la classe ResultsView
. Je sais que je ne peux pas.
Cependant, vous pouvez voir que le contenu du test est presque le même que celui de la classe DetaiView
que je viens d'écrire.
La seule différence est que la partie polls: detail
est remplacée par polls: results
.
En revanche, le document officiel considère que la duplication est inhérente au test et présente les trois points suivants comme règles d'organisation.
--Split TestClass
par modèle ou vue
--Créez différentes méthodes de test pour chaque ensemble de conditions que vous souhaitez tester
Je vous laisse avec les connaissances dont vous avez besoin pour exécuter ce didacticiel de test.
・ Du conteneur à l'hôte
nom ou ID du conteneur docker cp: chemin vers le répertoire ou le fichier dans lequel vous souhaitez copier le répertoire de l'hôte vers lequel copier
・ De l'hôte au conteneur
docker cp chemin vers le répertoire ou le fichier que vous souhaitez copier Nom ou ID du conteneur: chemin à enregistrer dans le conteneur
docker exec -nom du conteneur/bin/bash
Entrez dans le conteneur avec etc.
py -c "import django; print(django.__path__)"
Vous devez donc rechercher le chemin du fichier source. Vous pouvez trouver l'emplacement du dossier Django en recherchant le chemin du fichier source, et si vous le copiez du côté hôte, vous pouvez le trouver du côté hôte dans le pire des cas. Bien sûr, si vous connaissez le répertoire jusqu'au fichier, vous pouvez simplement l'ajouter et copier uniquement ce fichier directement.
J'ai étudié le code de test pour la première fois cette fois, mais j'ai senti que c'était assez profond parce que je pouvais enfin m'appeler un codeur après avoir écrit ceci. La vue basée sur les classes était également déroutante, mais je ne pense pas que cela soit suffisant. Cependant, lorsque j'ai écrit le code de test de cette manière, j'ai reconfirmé le code que j'avais écrit jusqu'à présent, et j'ai senti que c'était important parce que j'ai approfondi à nouveau ma compréhension, y compris les parties qui n'étaient pas claires. En complément du didacticiel, il y a un chapitre qui se penche uniquement sur le test, donc j'aimerais le lire après la fin des chapitres 6 et 7 suivants.
[Docker] Copier les fichiers entre le conteneur et l'hôte [[Django] Résumé des tests automatisés](https://qiita.com/okoppe8/items/eb7c3be5b9f6be244549#%E5%87%A6%E7%90%86%E3%81%AE%E6%B5%81% E3% 82% 8C) Création de la première application Django, partie 5
Recommended Posts