Wir bringen Ihnen insgesamt 5 Mal das Know-how bei, das für eine testgetriebene Entwicklung mit Flask erforderlich ist (dies ist geplant und kann sich ändern). In diesem zweiten Artikel werde ich Ihnen zeigen, wie Sie mit einem Dekorateur die Codemenge in Ihrem Testcode komprimieren.
Testcode für in Flask geschriebene APIs mit mehreren Endpunkten, Die Codemenge wird mithilfe eines Dekorators komprimiert.
Platzieren Sie den in diesem Artikel verwendeten Beispielcode in der folgenden Verzeichnisstruktur.
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 für eine Flask-App mit 3 Endpunkten.
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)
Für flask_app.py Testcode, der drei Testfälle implementiert.
test1.py
import sys
sys.path.append('../')
import flask_app
import unittest
class Test_flask_app_Normales System(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_Zugang zur Welt haben(self):
# 1.Definieren Sie testfallspezifische Variablen
self.DATA = b"Hello, World!"
self.ROUTE = "hello_world"
# 2.Gemeinsame Teile von Testfällen
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_Zugang zum Morgen(self):
# 1.Definieren Sie testfallspezifische Variablen
self.DATA = b"Good, Morning!"
self.ROUTE = "good_morning"
# 2.Gemeinsame Teile von Testfällen
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_Zugang zur Nacht(self):
# 1.Definieren Sie testfallspezifische Variablen
self.DATA = b"Good, Night!"
self.ROUTE = "good_night"
# 2.Gemeinsame Teile von Testfällen
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()
Für flask_app.py Testcode, der drei Testfälle implementiert. Der Inhalt des durchgeführten Tests ist der gleiche wie test1.py, Dieser Code verwendet einen Dekorateur.
test2.py
import unittest
# 1.Laden Sie den Dekorateur
from decorators import *
#Der Klassenname funktioniert auf Japanisch
class Test_flask_app_Normales System(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.Dekorateur ändern
@get_test()
def test_1_hello_Zugang zur Welt haben(self):
# 3.Definieren Sie testfallspezifische Variablen
self.DATA = b"Hello, World!"
self.ROUTE = "hello_world"
return
# 2.Dekorateur ändern
@get_test()
def test_2_good_Zugang zum Morgen(self):
# 3.Definieren Sie testfallspezifische Variablen
self.DATA = b"Good, Morning!"
self.ROUTE = "good_morning"
return
# 2.Dekorateur ändern
@get_test()
def test_3_good_Zugang zur Nacht(self):
# 3.Definieren Sie testfallspezifische Variablen
self.DATA = b"Good, Night!"
self.ROUTE = "good_night"
return
if __name__ == '__main__':
unittest.main()
Dekorator in test2.py verwendet.
decorators.py
import sys
sys.path.append('../')
import flask_app
#Definition des Dekorateurs
def get_test():
#Empfangen einer Funktion zum Testen
def recv_func(test_func):
#Dekorieren Sie die empfangene Testfunktion
def wrapper(self):
# 1.Testfall aufrufen
test_func(self)
# 2.Aggregation gemeinsamer Prozesse
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
Überprüfen Sie [Verzeichnisstruktur](# Verzeichnisstruktur) und führen Sie den folgenden Befehl aus.
$ ls
Dockerfile app
$ docker build -t pytest .
~Kürzung~
$ 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
In 1. werden die in jedem Testfall verwendeten Variablen initialisiert. Dieses Mal werden die Endpunkt- und API-Rückgabewerte gespeichert. Der Endpunkt und der Rückgabewert der API unterscheiden sich, da der Endpunkt und der Rückgabewert der zu testenden API unterschiedlich sind. Die Initialisierung wird für jeden Testfall mit unterschiedlichen Werten durchgeführt.
Führen Sie in 2. mit dem Testclient die API von flask_app.py aus und speichern Sie den Rückgabewert als Antwort. Nachfolgende Assert-Anweisungen werden verwendet, um den in der Antwort gespeicherten Status, Statuscode und Rückgabewert zu vergleichen. Der gleiche Vorgang wird in den drei Testfällen durchgeführt.
Insbesondere, weil die Teile, die jedem in 2. erläuterten Testfall gemeinsam sind, nicht aggregiert werden. Auch wenn es sich um eine geringfügige Korrektur handelt, muss diese in allen Testfällen korrigiert werden. Infolgedessen besteht die Möglichkeit, dass das Ändern des Testcodes einige Zeit in Anspruch nimmt, wenn eine testgetriebene Entwicklung durchgeführt wird.
Zum Beispiel, wenn aus irgendeinem Grund die Person, die diesen Testcode entwickelt hat, weg ist Der Nachfolger verfügt über eine große Menge an Testcode, dessen Entschlüsselung lange dauern kann. In diesem test1.py-Beispiel gibt es nur drei Testfälle: Kommerzieller Testcode hat wahrscheinlich viele Testfälle. Daher kann diese Schreibmethode die Wartungskosten erhöhen.
Um die Nachteile von test1.py zu lösen Aggregieren Sie den Testcode mit einem Dekorateur.
Ein Testfall, der mit der verschachtelten Funktion der obersten Ebene (get_test ()
) dekoriert ist.
Es kann mit test_func (self)
ausgeführt werden.
Da das Argument self dasselbe ist wie das Zieltestfall self,
Die durch "setUp (self)" definierte Eigenschaft kann vererbt werden.
Der im Wrapper geschriebene Prozess ist Es kann in vom Dekorateur modifizierten Testfällen verwendet werden. Das heißt, indem Sie den häufig verwendeten Prozess in "wrapper (self)" schreiben, Es ist möglich, die Codemenge zu komprimieren. Dieses Mal kann der Prozess des Vergleichens des Status, des Statuscodes und des Rückgabewerts unter Verwendung des Testclients und der assert-Anweisung, die üblicherweise in Testfällen verwendet werden, gemeinsam verwendet werden.
Laden Sie alle in decorators.py definierten Decorator-Funktionen.
Ändern Sie den Testfall mit "get_test ()", das in decorators.py definiert ist.
Die Endpunkt- und API-Rückgabewerte sind daher für jeden Testfall unterschiedlich Es kann nicht in einem Dekorateur zusammengefasst werden. Daher wird es im Testfall initialisiert.
Es ist möglich, die unter [Nachteile des Schreibens von test1.py](## Nachteile des Schreibens von test1.py) beschriebenen Nachteile zu verringern.
Wir haben eine Methode eingeführt, um die Codemenge im Testcode zu reduzieren, indem wir einen Dekorator für den Testcode verwenden. Durch einfaches Umschreiben des Inhalts von "wrapper (self)" in test2.py kann es mit anderen Flask-Apps verwendet werden.
Ich werde einen Artikel über Fukahori über die get-Methode des Testclients und den abnormalen Systemtest schreiben.
Recommended Posts