Au cours des 5 fois (il est prévu et sujet à changement), nous vous enseignerons le savoir-faire nécessaire pour un développement piloté par les tests avec Flask. Dans ce deuxième article, je vais vous montrer comment utiliser un décorateur pour compresser la quantité de code dans votre code de test.
1er Tutoriel pour faire du développement piloté par les tests (TDD) avec l'édition client de test Flask-1 2e article 3e écriture 4ème écriture 5ème écriture
Code de test pour les API écrites dans Flask avec plusieurs points de terminaison, La quantité de code est compressée à l'aide d'un décorateur.
Placez l'exemple de code utilisé dans cet article dans la structure de répertoires suivante.
flask_02/
├── Dockerfile
└── app
├── flask_app.py
└── test
├── decorators.py
├── test1.py
└── test2.py
$ docker --version
Docker version 19.03.12, build 48a66213fe
Dockerfile
Dockerfile
FROM python:3.6
USER root
RUN apt update
RUN /usr/local/bin/python -m pip install --upgrade pip
RUN pip install flask==1.1.2
COPY ./app /root/
WORKDIR /root/test
Code pour une application Flask avec 3 points de terminaison.
flask_app.py
from flask import Flask
app = Flask(__name__)
@app.route('/hello_world')
def hello_world():
return 'Hello, World!'
@app.route('/good_morning')
def good_morning():
return 'Good, Morning!'
@app.route('/good_night')
def good_night():
return 'Good, Night!'
if __name__ == '__main__':
app.run(host="0.0.0.0",port=5000)
Pour flask_app.py Code de test qui implémente trois cas de test.
test1.py
import sys
sys.path.append('../')
import flask_app
import unittest
class Test_flask_app_Système normal(unittest.TestCase):
def setUp(self):
self.ENDPOINT = "http://localhost:5000/{}"
self.DATA = None
self.STATUS = "200 OK"
self.STATUS_CODE = 200
self.ROUTE = None
def test_1_hello_Pouvoir accéder au monde(self):
# 1.Définir des variables spécifiques au cas de test
self.DATA = b"Hello, World!"
self.ROUTE = "hello_world"
# 2.Parties communes des cas de test
with flask_app.app.test_client() as client:
response = client.get(self.ENDPOINT.format(self.ROUTE))
assert response.data == self.DATA
assert response.status == self.STATUS
assert response.status_code == self.STATUS_CODE
return
def test_2_good_Accès au matin(self):
# 1.Définir des variables spécifiques au cas de test
self.DATA = b"Good, Morning!"
self.ROUTE = "good_morning"
# 2.Parties communes des cas de test
with flask_app.app.test_client() as client:
response = client.get(self.ENDPOINT.format(self.ROUTE))
assert response.data == self.DATA
assert response.status == self.STATUS
assert response.status_code == self.STATUS_CODE
return
def test_3_good_Accès à la nuit(self):
# 1.Définir des variables spécifiques au cas de test
self.DATA = b"Good, Night!"
self.ROUTE = "good_night"
# 2.Parties communes des cas de test
with flask_app.app.test_client() as client:
response = client.get(self.ENDPOINT.format(self.ROUTE))
assert response.data == self.DATA
assert response.status == self.STATUS
assert response.status_code == self.STATUS_CODE
return
if __name__ == '__main__':
unittest.main()
Pour flask_app.py Code de test qui implémente trois cas de test. Le contenu du test en cours est le même que test1.py, Ce code utilise un décorateur.
test2.py
import unittest
# 1.Chargez le décorateur
from decorators import *
#Le nom de la classe fonctionne en japonais
class Test_flask_app_Système normal(unittest.TestCase):
def setUp(self):
self.ENDPOINT = "http://localhost:5000/{}"
self.DATA = None
self.STATUS = "200 OK"
self.STATUS_CODE = 200
self.ROUTE = None
# 2.Modifier le décorateur
@get_test()
def test_1_hello_Pouvoir accéder au monde(self):
# 3.Définir des variables spécifiques au cas de test
self.DATA = b"Hello, World!"
self.ROUTE = "hello_world"
return
# 2.Modifier le décorateur
@get_test()
def test_2_good_Accès au matin(self):
# 3.Définir des variables spécifiques au cas de test
self.DATA = b"Good, Morning!"
self.ROUTE = "good_morning"
return
# 2.Modifier le décorateur
@get_test()
def test_3_good_Accès à la nuit(self):
# 3.Définir des variables spécifiques au cas de test
self.DATA = b"Good, Night!"
self.ROUTE = "good_night"
return
if __name__ == '__main__':
unittest.main()
Décorateur utilisé dans test2.py.
decorators.py
import sys
sys.path.append('../')
import flask_app
#Définition de décorateur
def get_test():
#Recevoir une fonction pour tester
def recv_func(test_func):
#Décorez la fonction de test reçue
def wrapper(self):
# 1.Cas de test d'appel
test_func(self)
# 2.Agrégation de processus communs
with flask_app.app.test_client() as client:
response = client.get(self.ENDPOINT.format(self.ROUTE))
assert response.data == self.DATA
assert response.status == self.STATUS
assert response.status_code == self.STATUS_CODE
return wrapper
return recv_func
Vérifiez [Structure du répertoire](# structure du répertoire) et exécutez la commande suivante.
$ ls
Dockerfile app
$ docker build -t pytest .
~réduction~
$ docker run -it pytest /usr/local/bin/python /root/test/test1.py
...
----------------------------------------------------------------------
Ran 3 tests in 0.006s
OK
$ docker run -it pytest /usr/local/bin/python /root/test/test2.py
...
----------------------------------------------------------------------
Ran 3 tests in 0.007s
OK
En 1., les variables utilisées dans chaque cas de test sont initialisées. Cette fois, il stocke les valeurs de retour de point de terminaison et d'API. Le point de terminaison et la valeur de retour de l'API sont différents car le point de terminaison et la valeur de retour de l'API à tester sont différents. L'initialisation est effectuée avec des valeurs différentes pour chaque cas de test.
En 2., à l'aide du client de test, exécutez l'API de flask_app.py et stockez la valeur de retour en réponse. Les instructions d'assertion suivantes sont utilisées pour comparer l'état, le code d'état et la valeur de retour stockés dans la réponse. Le même processus est effectué dans les trois cas de test.
Plus précisément, parce que les parties communes à chaque cas de test expliqué en 2. ne sont pas agrégées. Même s'il s'agit d'une correction mineure, il est nécessaire de la corriger dans tous les cas de test. Par conséquent, il est possible que la modification du code de test prenne du temps lors du développement piloté par les tests.
Par exemple, si pour une raison quelconque la personne qui a développé ce code de test est partie Le successeur a une grande quantité de code de test, qui peut prendre du temps à déchiffrer. De plus, dans cet exemple test1.py, il n'y a que trois cas de test, Le code de test commercial est susceptible d'avoir de nombreux cas de test. Par conséquent, cette méthode d'écriture peut augmenter le coût de maintenance.
Pour résoudre les inconvénients de test1.py Agrégez le code de test à l'aide d'un décorateur.
Un cas de test décoré avec la fonction imbriquée de niveau supérieur (get_test ()
),
Il peut être exécuté avec test_func (self)
.
De plus, puisque l'argument self est le même que le cas de test cible self,
La propriété définie par setUp (self)
peut être héritée.
Le processus écrit dans le wrapper est
Il peut être utilisé dans des cas de test modifiés par un décorateur.
Autrement dit, en écrivant le processus couramment utilisé dans wrapper (self)
,
Il est possible de compresser la quantité de code.
Cette fois, le processus de comparaison de l'état, du code d'état et de la valeur de retour à l'aide du client de test et de l'instruction assert, qui sont couramment utilisés dans le cas de test, peut être utilisé en commun.
Chargez toutes les fonctions de décorateur définies dans decorators.py.
Modifiez le cas de test avec get_test ()
défini dans decorators.py.
Les valeurs de retour du point de terminaison et de l'API sont différentes pour chaque scénario de test, donc Il ne peut pas être agrégé dans un décorateur. Par conséquent, il est initialisé dans le cas de test.
Il est possible de réduire les inconvénients décrits dans [Inconvénients de l'écriture de test1.py](## Inconvénients de l'écriture de test1.py).
Nous avons introduit une méthode pour réduire la quantité de code dans le code de test en utilisant un décorateur pour le code de test.
Vous pouvez l'utiliser avec d'autres applications Flask simplement en réécrivant le contenu de wrapper (self)
dans test2.py.
J'écrirai un article sur Fukahori de la méthode get du client de test et du test système anormal.
Recommended Posts