[PYTHON] Reverse Pull Pytest

Dies ist der Artikel zum 11. Tag von Python Part 2 Adventskalender 2015.

Einführung

In diesem Artikel werden die bei der Verwendung von pytest erhaltenen Tipps im umgekehrten Format zusammengefasst. Ein Beispielprojekt (bestätigt mit Python 3.5.0 / pytest 2.8.4), das den Inhalt dieses Artikels enthält, wird im folgenden Repository abgelegt. Lesen Sie daher auch dieses. https://github.com/FGtatsuro/pytest_sample

Merkmale des Pytests

pytest ist, wie der Name schon sagt, eine in Python geschriebene Testbibliothek. Ähnliche Bibliotheken sind unittest und Nase.

Da ich mit den beiden oben genannten Tools nicht vertraut bin, kann ich keine Bewertung im Vergleich zu ihnen vornehmen, aber ich persönlich war der Meinung, dass die folgenden Punkte charakteristisch sind.

pytest Tipps

Geben Sie Standardwerte für Optionen an

Sie können den Standardwert der Option für pytest in setup.cfg angeben.

setup.cfg


#Die Optionen, die angegeben werden können, sind py.test --Siehe Hilfe
[pytest]
addopts = -v

(Referenz) http://pytest.org/latest/customize.html?highlight=setup%20cfg#adding-default-options

Legen Sie eine Zeitüberschreitung für den Test fest

Der Pytest selbst verfügt nicht über eine Timeout-Funktion, kann jedoch vom Plug-In pytest-timeout unterstützt werden.

pytest-Installation des Timeout-Plug-Ins


$ pip install pytest-timeout

Es gibt verschiedene Möglichkeiten, das Zeitlimit festzulegen, die wie folgt kombiniert werden können.

Standard-Timeout-Zeit


[pytest]
addopts = -v
timeout = 5

Angeben der Zeitüberschreitungszeit durch Annotation


import time

@pytest.mark.timeout(10)
def test_timeout():
    time.sleep(8)

Laufzeitprotokolle in der Standardausgabe anzeigen

Wenn der HTTP-Zugriff in einem Integrationstest usw. erfolgt, ist es zweckmäßig, die HTTP-Anforderung / Antwort zum Zeitpunkt der Testausführung in der Standardausgabe bestätigen zu können (in vielen Fällen zumindest bei der Implementierung des Tests). Am Beispiel von Anfragen kann der Zweck erreicht werden, indem ein Handler wie folgt definiert wird.

Beispiele für die Handler-Implementierung für Anforderungen


import requests
# _Die Protokollierung ist ein selbst definiertes Modul
from ._logging import get_logger

logger = get_logger(__name__)

class ResponseHandler(object):

    def __call__(self, resp, *args, **kwargs):
        logger.debug('### Request ###')
        logger.debug('Method:{0}'.format(resp.request.method))
        logger.debug('URL:{0}'.format(resp.request.url))
        logger.debug('Header:{0}'.format(resp.request.headers))
        logger.debug('Body:{0}'.format(resp.request.body))
        logger.debug('### Response ###')
        logger.debug('Status:{0}'.format(resp.status_code))
        logger.debug('Header:{0}'.format(resp.headers))
        logger.debug('Body:{0}'.format(resp.text))

class HttpBinClient(object):
    '''
    The client for https://httpbin.org/
    '''

    base = 'https://httpbin.org'

    def __init__(self):
        self.session = requests.Session()
        #Registrieren Sie den erstellten Handler
        # http://docs.python-requests.org/en/latest/user/advanced/?highlight=response#event-hooks
        self.session.hooks = {'response': ResponseHandler()}
    
    def ip(self):
        return self.session.get('{0}/ip'.format(self.base))

Mit den Standardeinstellungen von pytest wird der Inhalt der Standardausgabe bei Ausführung des Tests jedoch von pytest erfasst (wird zum Melden der Testergebnisse verwendet), sodass die Ausgabe des Handlers nicht so bestätigt werden kann, wie sie ist. Um dies zu überprüfen, müssen Sie den Wert der Option "Capture" auf "No" setzen.

--capture=Ausführungsbeispiel von Nr


$ py.test --capture=no
=========================================================================================== test session starts ============================================================================================
...
tests/test_calc.py::test_add
PASSED
tests/test_client.py::test_ip
DEBUG:sample.client:2015-12-10 11:26:17,265:### Request ###
DEBUG:sample.client:2015-12-10 11:26:17,265:Method:GET
DEBUG:sample.client:2015-12-10 11:26:17,265:URL:https://httpbin.org/ip
DEBUG:sample.client:2015-12-10 11:26:17,265:Header:{'Accept-Encoding': 'gzip, deflate', 'Connection': 'keep-alive', 'User-Agent': 'python-requests/2.8.1', 'Accept': '*/*'}
DEBUG:sample.client:2015-12-10 11:26:17,265:Body:None
DEBUG:sample.client:2015-12-10 11:26:17,265:### Response ###
DEBUG:sample.client:2015-12-10 11:26:17,265:Status:200
DEBUG:sample.client:2015-12-10 11:26:17,265:Header:{'Content-Type': 'application/json', 'Access-Control-Allow-Origin': '*', 'Server': 'nginx', 'Access-Control-Allow-Credentials': 'true', 'Content-Length': '33', 'Connection': 'keep-alive', 'Date': 'Thu, 10 Dec 2015 02:26:17 GMT'}
DEBUG:sample.client:2015-12-10 11:26:17,315:Body:{
  "origin": xxxxx
}
...

Wenn es schwierig ist, die Option jedes Mal anzugeben, empfiehlt es sich, den Standardwert anzugeben.

Standardwert für die Erfassungsoption


[pytest]
addopts = -v --capture=no
timeout = 5

(Referenz) http://pytest.org/latest/capture.html?highlight=capture

Arbeite mit Jenkins

Mit der Option "junit-xml" können Sie einen Testbericht im XUnit-Format in eine angegebene Datei ausgeben. Dies macht es einfach, mit Jenkins zu arbeiten.

Berichtsausgabe im XUnit-Format


[pytest]
addopts = -v --capture=no --junit-xml=results/results.xml
timeout = 5

Bei der Überprüfung der Testergebnisse mit Jenkins ist es zweckmäßig, die einzelnen Testmethoden und die Standardausgabe zu diesem Zeitpunkt überprüfen zu können. Wie oben erwähnt, enthält der Testbericht nicht die Standardausgabe, wenn der Standardwert der Option "Capture" auf "Nein" gesetzt ist. Daher ist es besser, den Wert zur Laufzeit zu überschreiben.

--capture=Geben Sie sys zur Laufzeit an


$ py.test --capture=sys
=========================================================================================== test session starts ============================================================================================
...
tests/test_calc.py::test_add PASSED
#HTTP-Anfrage/Kein Antwortprotokoll
tests/test_client.py::test_ip PASSED
tests/test_timeout.py::test_timeout PASSED
...

#HTTP-Anforderung für Testbericht/Enthält ein Antwortprotokoll
$ cat results/results.xml
<?xml version="1.0" encoding="utf-8"?><testsuite errors="0" failures="0" name="pytest" skips="0" tests="3" time="9.110"><testcase classname="tests.test_calc" file="tests/test_calc.py" line="5" name="test_add" time="0.00024390220642089844"><system-out>
</system-out></testcase><testcase classname="tests.test_client" file="tests/test_client.py" line="7" name="test_ip" time="0.9390749931335449"><system-out>
DEBUG:sample.client:2015-12-10 12:29:33,753:### Request ###
DEBUG:sample.client:2015-12-10 12:29:33,754:Method:GET
DEBUG:sample.client:2015-12-10 12:29:33,754:URL:https://httpbin.org/ip
DEBUG:sample.client:2015-12-10 12:29:33,754:Header:{&apos;Connection&apos;: &apos;keep-alive&apos;, &apos;User-Agent&apos;: &apos;python-requests/2.8.1&apos;, &apos;Accept-Encoding&apos;: &apos;gzip, deflate&apos;, &apos;Accept&apos;: &apos;*/*&apos;}
DEBUG:sample.client:2015-12-10 12:29:33,754:Body:None
DEBUG:sample.client:2015-12-10 12:29:33,754:### Response ###
DEBUG:sample.client:2015-12-10 12:29:33,754:Status:200
DEBUG:sample.client:2015-12-10 12:29:33,754:Header:{&apos;Content-Type&apos;: &apos;application/json&apos;, &apos;Date&apos;: &apos;Thu, 10 Dec 2015 03:29:34 GMT&apos;, &apos;Connection&apos;: &apos;keep-alive&apos;, &apos;Access-Control-Allow-Origin&apos;: &apos;*&apos;, &apos;Content-Length&apos;: &apos;33&apos;, &apos;Access-Control-Allow-Credentials&apos;: &apos;true&apos;, &apos;Server&apos;: &apos;nginx&apos;}
DEBUG:sample.client:2015-12-10 12:29:33,811:Body:{
  &quot;origin&quot;: &quot;124.33.163.178&quot;
}

</system-out></testcase><testcase classname="tests.test_timeout" file="tests/test_timeout.py" line="8" name="test_timeout" time="8.001494884490967"><system-out>
</system-out></testcase></testsuite>% 

Führen Sie den Test über setuptools aus

Durch die Verknüpfung mit setuptools können Sie Tests ausführen, ohne die Ausführungsumgebung zu verschmutzen (ohne den Inhalt von = pip freeze zu ändern). Ich denke, dies ist in dem Sinne nützlich, dass Sie unnötige Probleme vermeiden können, wenn Sie die Möglichkeit haben, den Test anders als Sie selbst auszuführen, und der Executor Python nicht genug verwendet, um die Umgebung mit "virtualenv" zu schneiden. Ein Beispiel für "setup.py" ist unten gezeigt.

setup.py(Zusammenarbeit zwischen Setup-Tools und Pytest)


import os
import sys

from setuptools import setup, find_packages
from setuptools.command.test import test as TestCommand

#Implementierung eines Befehls zur Ausführung von Pytest
class PyTest(TestCommand):

    #Bei der Angabe von Pytest-Optionen--pytest-args='{options}'Benutzen
    user_options = [
        ('pytest-args=', 'a', 'Arguments for pytest'),
    ]

    def initialize_options(self):
        TestCommand.initialize_options(self)
        self.pytest_target = []
        self.pytest_args = []

    def finalize_options(self):
        TestCommand.finalize_options(self)
        self.test_args = []
        self.test_suite = True

    def run_tests(self):
        import pytest
        errno = pytest.main(self.pytest_args)
        sys.exit(errno)

version = '0.1'

# setup.Pytest muss in py importiert werden
setup_requires=[
    'pytest'
]
install_requires=[
    'requests',
]
tests_require=[
    'pytest-timeout',
    'pytest'
]

setup(name='pytest_sample',
      ...
      setup_requires=setup_requires,
      install_requires=install_requires,
      tests_require=tests_require,
      # 'test'Ist mit "Befehl zum Ausführen von Pytest" verbunden
      cmdclass={'test': PyTest},
)

Nachdem Sie setup.py geändert haben, können Sie den Test mit python setup.py test ausführen.

Testausführung über Setup-Tools


$ python setup.py test
...
tests/test_calc.py::test_add
PASSED
tests/test_client.py::test_ip
DEBUG:sample.client:2015-12-10 12:54:20,426:### Request ###
DEBUG:sample.client:2015-12-10 12:54:20,426:Method:GET
DEBUG:sample.client:2015-12-10 12:54:20,426:URL:https://httpbin.org/ip
DEBUG:sample.client:2015-12-10 12:54:20,426:Header:{'Connection': 'keep-alive', 'User-Agent': 'python-requests/2.8.1', 'Accept': '*/*', 'Accept-Encoding': 'gzip, deflate'}
DEBUG:sample.client:2015-12-10 12:54:20,426:Body:None
DEBUG:sample.client:2015-12-10 12:54:20,426:### Response ###
DEBUG:sample.client:2015-12-10 12:54:20,426:Status:200
DEBUG:sample.client:2015-12-10 12:54:20,426:Header:{'Server': 'nginx', 'Access-Control-Allow-Origin': '*', 'Access-Control-Allow-Credentials': 'true', 'Connection': 'keep-alive', 'Content-Length': '33', 'Date': 'Thu, 10 Dec 2015 03:54:20 GMT', 'Content-Type': 'application/json'}
DEBUG:sample.client:2015-12-10 12:54:20,484:Body:{
  "origin": "124.33.163.178"
}

PASSED
tests/test_timeout.py::test_timeout
PASSED
...

Wenn Sie Pytest-Optionen angeben möchten, verwenden Sie die in der obigen Implementierung definierte Option "--pytest-args".

Testausführung über Setup-Tools(Mit Optionen)


$ python setup.py test --pytest-args='--capture=sys'
...
tests/test_calc.py::test_add PASSED
tests/test_client.py::test_ip PASSED
tests/test_timeout.py::test_timeout PASSED
...

(Referenz) http://pytest.org/latest/goodpractises.html

(Ergänzung) Als ich das Dokument beim Schreiben erneut las, stellte ich fest, dass es eine Bibliothek gibt, die dasselbe tut. Es kann gut sein, dies zu verwenden. https://pypi.python.org/pypi/pytest-runner

Führen Sie Tests parallel durch

Mit dem Plug-In pytest-xdist können Sie Tests parallel ausführen.

Parallele Ausführung von Tests


#Parallele Ausführung in 2 Prozessen
$ py.test -n 2 

Da es in einem separaten Prozess ausgeführt wird, muss die Abhängigkeit der Bibliothek in der Umgebung, in der es ausgeführt wird, im Voraus aufgelöst werden. Daher ist es nicht mit der Ausführung über setuptools kompatibel, wodurch Abhängigkeiten zur Laufzeit aufgelöst werden.

Führen Sie nur Tests erneut aus, die im vorherigen Lauf fehlgeschlagen sind

Mit der Option "lf" können Sie nur die Tests wiederholen, die im vorherigen Lauf fehlgeschlagen sind.

Informationen zu fehlgeschlagenen Tests werden in ".cache / v / cache / lastfailed" direkt unter dem Verzeichnis aufgezeichnet, in dem der Test ausgeführt wurde. Wenn Sie mit anderen Tools arbeiten möchten (z. B. Nur bei fehlgeschlagenem Test erneut ausführen), möchten Sie möglicherweise direkt auf diese Datei verweisen.

$ py.test --capture=sys
collected 3 items
...
tests/test_calc.py::test_add PASSED
tests/test_client.py::test_ip PASSED
tests/test_timeout.py::test_timeout FAILED
...
#Informationen zu fehlgeschlagenen Tests
$ cat .cache/v/cache/lastfailed
{
  "tests/test_timeout.py::test_timeout": true
}

#Testfix...
 
#Führen Sie nur fehlgeschlagene Tests erneut aus
$ py.test --capture=sys --lf

Filtern Sie die auszuführenden Tests

pytest kann die auszuführenden Tests nach verschiedenen Bedingungen filtern.

Zunächst aus der Methode zur Angabe des zu spielenden Moduls / der zu spielenden Methode.

Modul/Methodenspezifikation


#Geben Sie das Modul an
$ py.test tests/test_calc.py
#Geben Sie im Modul eine Methode an
$ py.test tests/test_calc.py::test_add

Sie können auch nur die Module / Methoden ausführen, die der Zeichenfolge entsprechen

Filtern nach String-Matching


# 'calc'Modul mit der Zeichenfolge/Führen Sie die Methode aus
$ py.test -k calc

Sie können auch nach der Markierung des Dekorateurs filtern. Da es und / oder unterstützt, ist es möglich, Bedingungen wie mit / ohne Mehrfachmarkierungen anzugeben.

Filtern nach Markierung


#Markierung: pytest.mark.<Es kann eine beliebige Zeichenfolge angegeben werden>
@pytest.mark.slow
@pytest.mark.httpaccess
def test_ip():
    ...

@pytest.mark.slow
@pytest.mark.timeout(10)
def test_timeout():
    ...

# @pytest.mark.Führen Sie Tests mit langsam durch
$ py.test -m slow
# @pytest.mark.slow/@pytest.mark.Führen Sie einen Test mit beiden httpaccess aus
$ py.test -m 'slow and httpaccess'
# @pytest.mark.Es hat langsam, [email protected]ühren Sie Tests ohne httpaccess aus
$ py.test -m 'slow and not httpaccess'

(Referenz) https://pytest.org/latest/example/markers.html#mark-examples

abschließend

Es gibt verschiedene andere Funktionen. Wenn Sie interessiert sind, sollten Sie das Offizielle Dokument lesen.

Morgen 12 ist @shinyorke.

Recommended Posts

Reverse Pull Pytest
Sympy Reverse Reference
Pandas Reverse Memo
Luigi Reverse Reference
pytest
Tipps zur Servereinstellung Reverse Pull
Django Management Screen Reverse Memo
Reverse Reference der Python-Datums- / Zeitbibliothek
pytest memo
pytest Zusammenfassung
Asynchrone Verarbeitung in Python: Asyncio-Reverse-Referenz