[PYTHON] Tutorial für die testgetriebene Entwicklung (TDD) mit Flask-2-Dekorateuren

Einführung

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.

  1. Tutorial für die testgetriebene Entwicklung (TDD) mit der Flask-1-Testclient-Edition
  2. Artikel
  3. Schreiben
  4. Schreiben
  5. Schreiben

Zielgruppe

Überblick

Testcode für in Flask geschriebene APIs mit mehreren Endpunkten, Die Codemenge wird mithilfe eines Dekorators komprimiert.

Verzeichnisaufbau

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
Docker version 19.03.12, build 48a66213fe

Code-Vorbereitung

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

flask_app.py (Testziel)

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)

test1.py (Testcode)

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()

test2.py (Testcode)

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()

decorators.py

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

Führen Sie den Test aus

Ü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

Erklärung von test1.py (Testcode)

1. Definieren Sie testfallspezifische Variablen

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.

2. Gemeinsame Teile von Testfällen

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.

Nachteile des Schreibens von test1.py

① Erhöhte Kosten für testgetriebene Entwicklung

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.

② Die Kosten für die Wartung des Testcodes steigen

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.

Erklärung von decorators.py (Dekorator) und test2.py (Testcode)

Um die Nachteile von test1.py zu lösen Aggregieren Sie den Testcode mit einem Dekorateur.

Erklärung von decorators.py

1. Testfall aufrufen

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.

2. Aggregation gemeinsamer Prozesse

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.

Erklärung von test2.py (Testcode)

1. Laden Sie den Dekorator

Laden Sie alle in decorators.py definierten Decorator-Funktionen.

2. Ändern Sie den Dekorator

Ändern Sie den Testfall mit "get_test ()", das in decorators.py definiert ist.

3. Definieren Sie testfallspezifische Variablen

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.

Vorteile der Verwendung von Dekoratoren im Testcode

Es ist möglich, die unter [Nachteile des Schreibens von test1.py](## Nachteile des Schreibens von test1.py) beschriebenen Nachteile zu verringern.

Zusammenfassung

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.

nächstes Mal

Ich werde einen Artikel über Fukahori über die get-Methode des Testclients und den abnormalen Systemtest schreiben.

Recommended Posts

Tutorial für die testgetriebene Entwicklung (TDD) mit Flask-2-Dekorateuren
Tutorial für die testgetriebene Entwicklung (TDD) mit Flask-1 Test Client Edition
[Test Driven Development (TDD)] Kapitel 21 Zusammenfassung
Testcode zur Bewertung von Dekorateuren
Lernverlauf für die Teilnahme an der Team-App-Entwicklung mit Python ~ Django Tutorial 5 ~
Lernverlauf für die Teilnahme an der Entwicklung von Team-Apps mit Python ~ Django Tutorial 4 ~
Lernverlauf für die Teilnahme an der Team-App-Entwicklung mit Python ~ Django Tutorial 1, 2, 3 ~
Lernverlauf für die Teilnahme an der Team-App-Entwicklung mit Python ~ Django Tutorial 6 ~
Lernverlauf für die Teilnahme an der Team-App-Entwicklung mit Python ~ Django Tutorial 7 ~
Implementieren Sie einen tabellengesteuerten Test in Java
Testgetriebene Entwicklung mit Django Teil 4
Testgetriebene Entwicklung mit Django Teil 6
Testgetriebene Entwicklung mit Django Teil 2
Testgetriebene Entwicklung mit Django Teil 1
Testgetriebene Entwicklung mit Django Teil 5
Erleben Sie Teil I "Multinationale Währung" des Buches "Test Driven Development" mit Python
Testgetriebenes Entwicklungsstart mit PySide & Pytest
(Für mich) Setzen Sie den Kolben in den VS-Code ein
Tipps zum Erstellen großer Anwendungen mit Flask
Django Tutorial Zusammenfassung für Anfänger von Anfängern ⑤ (Test)