La dernière fois, Django Tutorial (Create Blog App) ③ --Article List Display est une vue générale basée sur les classes pour afficher une liste d'articles créés à partir du site de gestion. J'ai utilisé.
J'aimerais ajouter le traitement CRUD tel que la création d'article, les détails, l'édition et la suppression dans l'application telle quelle, mais retenons et incluons ** test unitaire **.
C'est amusant d'ajouter plus de fonctionnalités, mais écrivez-vous habituellement des tests?
Même ceux qui sont devenus capables de créer des applications Django simples grâce à divers tutoriels, etc. Je pense que cela peut provoquer une erreur lorsque vous jouez un peu avec. De plus, même si aucune erreur n'est générée lorsque Django est démarré avec runserver, etc. Vous pouvez remarquer une erreur lorsque vous déplacez réellement l'écran via le navigateur.
Bien sûr, vous pouvez tester manuellement certaines opérations, mais il est inutile de le faire à chaque fois.
Par conséquent, il est recommandé d'effectuer un test unitaire à l'aide de la fonction Django. Django vous permet d'automatiser les tests en utilisant la classe UnitTest, donc Une fois que vous n'avez écrit que le code de test en premier, vous n'avez pas à refaire la même chose encore et encore.
Penser aux tests est aussi important que penser au code de développement, Il existe même une méthode de développement consistant à créer un test puis à écrire le code pour le fonctionnement de l'application.
Maintenant que vous avez la possibilité de tester, économisez votre temps de test et travaillez dur pour améliorer l'application elle-même.
À ce stade, la structure des dossiers doit être la suivante.
.
├── blog
│ ├── __init__.py
│ ├── admin.py
│ ├── apps.py
│ ├── migrations
│ │ ├── 0001_initial.py
│ │ └── __init__.py
│ ├── models.py
│ ├── tests.py #Attention
│ ├── urls.py
│ └── views.py
├── db.sqlite3
├── manage.py
├── mysite
│ ├── __init__.py
│ ├── settings.py
│ ├── urls.py
│ └── wsgi.py
└── templates
└── blog
├── index.html
└── post_list.html
Comme vous l'avez peut-être remarqué, un fichier appelé ** tests.py ** est automatiquement créé sous le répertoire du blog.
Vous pouvez créer des cas de test directement dans ce tests.py, Il est plus facile de gérer si les fichiers sont séparés pour chaque test de modèle, test de vue et test. Créez un répertoire de tests comme indiqué ci-dessous et créez un fichier vide dans chacun d'eux. Le but est de créer un fichier ** __ init__.py ** à partir du contenu afin que les fichiers du répertoire tests soient également exécutés.
.
├── blog
│ ├── __init__.py
│ ├── admin.py
│ ├── apps.py
│ ├── migrations
│ │ ├── 0001_initial.py
│ │ └── __init__.py
│ ├── models.py
│ ├── tests #ajouter à
│ │ ├── __init__.py
│ │ ├── test_models.py
│ │ ├── test_urls.py
│ │ └── test_views.py
......
Veuillez noter que Django ne reconnaîtra pas le nom du module à moins qu'il ne commence par "test".
Django étend la classe TestCase standard Python (unittest.TestCase), Utilisez la propre classe TestCase de Django (django.test.TestCase). Dans cette classe, vous pouvez utiliser une méthode appelée assertion, qui a une fonction pour vérifier si la valeur de retour est la valeur attendue.
De plus, comme mentionné ci-dessus, le module de test doit commencer par la chaîne "test". La méthode de test doit également commencer par la chaîne "test" (nous en parlerons plus tard).
En suivant cette règle, Django pourra trouver la méthode de test dans votre projet et l'exécuter automatiquement.
test_models.py Commençons par tester le modèle. Pour récapituler, le modèle de publication décrit dans blog / models.py ressemble à ceci.
models.py
...
class Post(models.Model):
title = models.CharField('Titre', max_length=200)
text = models.TextField('Texte')
date = models.DateTimeField('Date', default=timezone.now)
def __str__(self): #Définit la valeur à renvoyer lorsque le modèle Post est appelé directement
return self.title #Renvoie le titre de l'article
Testons ce modèle avec les trois cas suivants cette fois.
Commençons par le premier.
Ouvrez test_models.py et déclarez les modules requis.
test_models.py
from django.test import TestCase
from blog.models import Post
Ensuite, nous allons créer une classe de test, mais assurez-vous d'en faire une classe qui hérite de TestCase.
test_models.py
from django.test import TestCase
from blog.models import Post
class PostModelTests(TestCase):
Maintenant, écrivons une méthode de test dans cette classe PostModelTest. En commençant par "test" dans une classe qui hérite de TestCase Django reconnaîtra automatiquement qu'il s'agit d'une méthode de test. Par conséquent, assurez-vous de nommer la méthode commençant par test après def.
test_models.py
from django.test import TestCase
from blog.models import Post
class PostModelTests(TestCase):
def test_is_empty(self):
"""Vérifiez que rien n'est enregistré dans l'état initial"""
saved_posts = Post.objects.all()
self.assertEqual(saved_posts.count(), 0)
Stocker le modèle Post actuel dans saved_posts Nous avons confirmé que le nombre de comptage (nombre d'articles) est "0" avec assertEqual.
Vous êtes maintenant prêt à faire un test. Exécutons-le une fois avec ça.
Pour exécuter le test, exécutez la commande suivante dans le répertoire (dans mysite) où se trouve manage.py. Lorsque vous l'exécutez, Django trouvera et exécutera une méthode de test qui suit la convention de dénomination.
(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
Cela signifie que vous avez exécuté un test et l'avez terminé sans aucune erreur.
En passant, j'ai confirmé que les données sont vides (= 0) dans Post plus tôt, mais attendons-nous à ce qu'il y ait une donnée.
test_models.py(temporaire)
from django.test import TestCase
from blog.models import Post
class PostModelTests(TestCase):
def test_is_empty(self):
"""État initial, mais il s'agit de vérifier si des données existent(une erreur est attendue)"""
saved_posts = Post.objects.all()
self.assertEqual(saved_posts.count(), 1)
Le résultat de l'exécution du test à ce moment est le suivant.
(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)
État initial, mais il s'agit de vérifier si des données existent(une erreur est attendue)
----------------------------------------------------------------------
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)
Vous obtenez une AssertionError et le test échoue car ce n'est pas ce à quoi vous vous attendiez (c'est une expérience réussie).
Dans le test de Django, vous pouvez également enregistrer temporairement des données dans la base de données à partir de la méthode create, donc Vous pouvez également exécuter le reste des tests que vous ne pourriez pas voir sans enregistrer les données. Vous trouverez ci-dessous comment rédiger un test de modèle, veuillez donc vous y référer.
test_models.py(Texte intégral)
from django.test import TestCase
from blog.models import Post
class PostModelTests(TestCase):
def test_is_empty(self):
"""Vérifiez que rien n'est enregistré dans l'état initial"""
saved_posts = Post.objects.all()
self.assertEqual(saved_posts.count(), 0)
def test_is_count_one(self):
"""Vérifiez que si vous créez correctement un enregistrement, un seul enregistrement sera compté"""
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):
"""Enregistrez les données avec le contenu spécifié et vérifiez que lorsque vous les récupérez immédiatement, la même valeur que lorsque vous les avez enregistrées est renvoyée."""
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 Outre le modèle, vous pouvez également vérifier si le routage que vous avez écrit dans urls.py fonctionne. Avec le recul, blog / urls.py ressemblait à ceci.
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'),
]
Dans le routage ci-dessus, le routage est défini en fonction de l'adresse saisie sous / blog /, donc / blog / Teste lorsque les éléments suivants sont '' (vide) et 'list'. Utilisez assertEqual pour comparer et vérifier si les résultats redirigés vers chaque page via la vue sont attendus.
test_urls.py
from django.test import TestCase
from django.urls import reverse, resolve
from ..views import IndexView, PostListView
class TestUrls(TestCase):
"""Redirection testée lors de l'accès par URL à la page d'index"""
def test_post_index_url(self):
view = resolve('/blog/')
self.assertEqual(view.func.view_class, IndexView)
"""Tester la redirection vers la page de liste d'articles"""
def test_post_list_url(self):
view = resolve('/blog/list')
self.assertEqual(view.func.view_class, PostListView)
Maintenant, exécutons le test une fois.
(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 Enfin, testons la vue.
views.py ressemble à ceci.
views.py
from django.views import generic
from .models import Post #Importer le modèle de poste
class IndexView(generic.TemplateView):
template_name = 'blog/index.html'
class PostListView(generic.ListView): #Hériter de la classe générique ListView
model = Post #Appelez le modèle que vous souhaitez lister
Le test IndexView vérifie que le code d'état 200 (= succès) est renvoyé lors de l'accès par la méthode GET.
test_views.py
from django.test import TestCase
from django.urls import reverse
from ..models import Post
class IndexTests(TestCase):
"""Classe de test IndexView"""
def test_get(self):
"""Confirmez que le code d'état 200 est renvoyé en accédant avec la méthode GET."""
response = self.client.get(reverse('blog:index'))
self.assertEqual(response.status_code, 200)
Lorsque vous ajoutez une méthode dans n'importe quelle vue, Peu importe le temps dont vous disposez pour rédiger un test, prenez l'habitude de le créer comme un cas de test minimum.
Nous allons également tester le ListView.
Sans parler de la confirmation que le code d'état 200 est également renvoyé. Ici, après avoir ajouté deux données (articles), la liste d'articles s'affiche et Créez un test pour vous assurer que chacun des titres d'articles enregistrés est inclus dans la liste.
Notez que nous utiliserons ici une méthode légèrement spéciale. J'ai mentionné plus tôt que la méthode de test commence par "test", mais il existe des méthodes ** setUp ** et ** tearDown **.
Dans la méthode setUp, enregistrez les données utilisées dans le cas de test et La méthode tearDown vous permet de supprimer les données enregistrées dans la méthode setUp. (Notez que les deux doivent indiquer explicitement les données à enregistrer)
L'écriture d'un processus qui enregistre des données plusieurs fois dans le même cas de test est un facteur qui prend du temps et du temps pour les tests. Le processus courant consiste à les rassembler en un seul endroit.
Si vous utilisez ces méthodes et créez test_views.py, cela ressemblera à ceci.
test_views.py
from django.test import TestCase
from django.urls import reverse
from ..models import Post
class IndexTests(TestCase):
"""Classe de test IndexView"""
def test_get(self):
"""Confirmez que le code d'état 200 est renvoyé en accédant avec la méthode GET."""
response = self.client.get(reverse('blog:index'))
self.assertEqual(response.status_code, 200)
class PostListTests(TestCase):
def setUp(self):
"""
Une méthode pour préparer l'environnement de test. Assurez-vous de le nommer "setUp".
S'il y a des données que vous souhaitez utiliser en commun dans la même classe de test, créez-les ici.
"""
post1 = Post.objects.create(title='title1', text='text1')
post2 = Post.objects.create(title='title2', text='text2')
def test_get(self):
"""Confirmez que le code d'état 200 est renvoyé en accédant avec la méthode GET."""
response = self.client.get(reverse('blog:list'))
self.assertEqual(response.status_code, 200)
def test_get_2posts_by_list(self):
"""Confirmez que 2 éléments supplémentaires ajoutés par la méthode setUp sont retournés lors de l'accès avec GET"""
response = self.client.get(reverse('blog:list'))
self.assertEqual(response.status_code, 200)
self.assertQuerysetEqual(
#Dans le modèle Post__str__Puisqu'il est configuré pour renvoyer le titre à la suite de, vérifiez si le titre renvoyé est comme publié
response.context['post_list'],
['<Post: title1>', '<Post: title2>'],
ordered = False #Spécifiez pour ignorer la commande
)
self.assertContains(response, 'title1') #Assurez-vous que le titre post1 est inclus dans le html
self.assertContains(response, 'title2') #Assurez-vous que le titre post2 est inclus dans le html
def tearDown(self):
"""
Une méthode de nettoyage qui efface les données ajoutées par setUp.
Bien qu'il soit "create", en définissant le nom de la méthode sur "tearDown", le traitement inverse de setUp est effectué = il est supprimé.
"""
post1 = Post.objects.create(title='title1', text='text1')
post2 = Post.objects.create(title='title2', text='text2')
Si vous exécutez le test dans cet état, 8 tests au total seront exécutés sur le modèle, l'URL et la vue.
(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'...
Vous avez maintenant créé un test unitaire pour le code que vous avez écrit jusqu'à présent. Si d'autres modèles attendus sont appelés, etc. Il existe également un moyen de vérifier de manière redondante avec un test en utilisant la propre méthode de test de Django. Prenez l'habitude d'écrire des tests avant d'écrire du code et évitez de vérifier plus tard.
La prochaine fois, je pourrai créer des articles dans l'application. Allons-y avec le style TDD (Test Driven Development) d'écrire le test unitaire que nous avons appris cette fois en premier.
Recommended Posts