PyTest vodič: što je, kako instalirati, okvir, tvrdnje
Što je PyTest?
PyTest je okvir za testiranje koji korisnicima omogućuje pisanje testnih kodova pomoću Python programski jezik. Pomaže vam u pisanju jednostavnih i skalabilnih testnih slučajeva za baze podataka, API-je ili korisničko sučelje. PyTest se uglavnom koristi za pisanje testova za API-je. Pomaže u pisanju testova od jednostavnih jediničnih testova do složenih funkcionalnih testova.
Zašto koristiti PyTest?
Neke od prednosti pytesta su
- Vrlo jednostavan za početak zbog jednostavne i lake sintakse.
- Može izvoditi testove paralelno.
- Može pokrenuti određeni test ili podskup testova
- Automatsko otkrivanje testova
- Preskoči testove
- Otvoreni izvor
Kako instalirati PyTest
Slijedi postupak kako instalirati PyTest:
Korak 1) Pytest možete instalirati putem
pip install pytest==2.9.1
Nakon dovršetka instalacije možete je potvrditi pomoću
py.test -h
Ovo će prikazati pomoć
Prvi osnovni PyTest
Sada ćemo naučiti kako koristiti Pytest s osnovnim primjerom PyTest-a.
Napravite mapu study_pytest. Napravit ćemo testne datoteke unutar ove mape.
Dođite do te mape u naredbenom retku.
Napravite datoteku pod nazivom test_sample1.py unutar mape
Dodajte donji kod u njega i spremite
import pytest def test_file1_method1(): x=5 y=6 assert x+1 == y,"test failed" assert x == y,"test failed" def test_file1_method2(): x=5 y=6 assert x+1 == y,"test failed"
Pokrenite test pomoću naredbe
py.test
Dobit ćete izlaz kao
test_sample1.py F.
============================================== FAILURES ========================================
____________________________________________ test_sample1 ______________________________________
def test_file1_method1():
x=5
y=6
assert x+1 == y,"test failed"
> assert x == y,"test failed"
E AssertionError: test failed
E assert 5 == 6
test_sample1.py:6: AssertionError
Ovdje u test_sample1.py F.
F kaže neuspjeh
Točka (.) govori o uspjehu.
U odjeljku neuspjeha možete vidjeti neuspješne metode i liniju neuspjeha. Ovdje x==y znači 5==6 što je netočno.
Zatim ćemo u ovom vodiču za PyTest naučiti o tvrdnji u PyTestu.
Tvrdnje u PyTestu
Pytest tvrdnje su provjere koje vraćaju status True ili False. U Python Pytest, ako tvrdnja ne uspije u testnoj metodi, tada se izvršenje te metode tamo zaustavlja. Preostali kod u toj metodi testiranja se ne izvršava, a Pytest tvrdnje će se nastaviti sa sljedećom metodom testiranja.
Primjeri Pytest Assert:
assert "hello" == "Hai" is an assertion failure. assert 4==4 is a successful assertion assert True is a successful assertion assert False is an assertion failure.
Uzeti u obzir
assert x == y,"test failed because x=" + str(x) + " y=" + str(y)
Postavite ovaj kod u test_file1_method1() umjesto tvrdnje
assert x == y,"test failed"
Pokretanje testa dat će neuspjeh kao AssertionError: test nije uspio x=5 y=6
Kako PyTest identificira testne datoteke i testne metode
Prema zadanim postavkama pytest identificira samo nazive datoteka koji počinju s test_ ili završava sa _test kao testne datoteke. Ipak, možemo eksplicitno spomenuti druga imena datoteka (objašnjeno kasnije). Pytest zahtijeva nazive testnih metoda za početak "test.” Sva druga imena metoda bit će zanemarena čak i ako izričito zatražimo pokretanje tih metoda.
Pogledajte neke primjere važećih i nevažećih naziva pytest datoteka
test_login.py - valid login_test.py - valid testlogin.py -invalid logintest.py -invalid
Napomena: Da, možemo izričito zatražiti od pytesta da izabere testlogin.py i logintest.py
Pogledajte neke primjere valjanih i nevažećih metoda testiranja pytest
def test_file1_method1(): - valid def testfile1_method1(): - valid def file1_method1(): - invalid
Napomena: čak i ako izričito spomenemo file1_method1() pytest neće pokrenuti ovu metodu.
Pokrenite više testova iz određene datoteke i više datoteka
Trenutno, unutar mape study_pytest, imamo datoteku test_sample1.py. Pretpostavimo da imamo više datoteka, recimo test_sample2.py, test_sample3.py. Da bismo pokrenuli sve testove iz svih datoteka u mapi i podmapama, moramo samo pokrenuti naredbu pytest.
py.test
Ovo će pokrenuti sve nazive datoteka koji počinju s test_ i nazive datoteka koji završavaju s _test u toj mapi i podmapama u toj mapi.
Za pokretanje testova samo iz određene datoteke, možemo koristiti py.test
py.test test_sample1.py
Pokrenite podskup cijelog testa s PyTestom
Ponekad ne želimo pokrenuti cijeli paket testova. Pytest nam omogućuje izvođenje specifičnih testova. Možemo to učiniti na 2 načina
- Grupiranje naziva testova prema podnisku podudaranja
- Grupiranje testova po markerima
Već imamo test_sample1.py. Napravite datoteku test_sample2.py i dodajte donji kod u nju
def test_file2_method1(): x=5 y=6 assert x+1 == y,"test failed" assert x == y,"test failed because x=" + str(x) + " y=" + str(y) def test_file2_method2(): x=5 y=6 assert x+1 == y,"test failed"
Tako da trenutno imamo
• test_sample1.py • test_file1_method1() • test_file1_method2() • test_sample2.py • test_file2_method1() • test_file2_method2()
Opcija 1) Pokrenite testove podudaranjem podniza
Ovdje moramo pokrenuti sve testove koji u nazivu imaju method1
py.test -k method1 -v -k <expression> is used to represent the substring to match -v increases the verbosity
Dakle, pokretanje py.test -k method1 -v će vam dati sljedeći rezultat
test_sample2.py::test_file2_method1 FAILED
test_sample1.py::test_file1_method1 FAILED
============================================== FAILURES ==============================================
_________________________________________ test_file2_method1 _________________________________________
def test_file2_method1():
x=5
y=6
assert x+1 == y,"test failed"
> assert x == y,"test failed because x=" + str(x) + " y=" + str(y)
E AssertionError: test failed because x=5 y=6
E assert 5 == 6
test_sample2.py:5: AssertionError
_________________________________________ test_file1_method1 _________________________________________
@pytest.mark.only
def test_file1_method1():
x=5
y=6
assert x+1 == y,"test failed"
> assert x == y,"test failed because x=" + str(x) + " y=" + str(y)
E AssertionError: test failed because x=5 y=6
E assert 5 == 6
test_sample1.py:8: AssertionError
================================= 2 tests deselected by '-kmethod1' ==================================
=============================== 2 failed, 2 deselected in 0.02 seconds ===============================
Ovdje možete vidjeti prema kraju 2 testa poništena su odabirom '-kmethod1' a to su test_file1_method2 i test_file2_method2
Pokušajte trčati s raznim kombinacijama kao što su:-
py.test -k method -v - will run all the four methods py.test -k methods -v – will not run any test as there is no test name matches the substring 'methods'
Opcija 2) Izvođenje testova prema markerima
Pytest nam omogućuje da postavimo različite atribute za testne metode pomoću pytest markera, @pytest.mark. Da bismo koristili oznake u testnoj datoteci, moramo uvesti pytest u testne datoteke.
Ovdje ćemo primijeniti različite nazive markera na metode testiranja i pokrenuti specifične testove na temelju naziva markera. Oznake na nazivima svakog testa možemo definirati pomoću
@pytest.mark.<name>.
Definiramo markere set1 i set2 na testnim metodama, a test ćemo pokrenuti pomoću imena markera. Ažurirajte testne datoteke sa sljedećim kodom
test_uzorak1.py
import pytest @pytest.mark.set1 def test_file1_method1(): x=5 y=6 assert x+1 == y,"test failed" assert x == y,"test failed because x=" + str(x) + " y=" + str(y) @pytest.mark.set2 def test_file1_method2(): x=5 y=6 assert x+1 == y,"test failed"
test_uzorak2.py
import pytest @pytest.mark.set1 def test_file2_method1(): x=5 y=6 assert x+1 == y,"test failed" assert x == y,"test failed because x=" + str(x) + " y=" + str(y) @pytest.mark.set1 def test_file2_method2(): x=5 y=6 assert x+1 == y,"test failed"
Možemo pokrenuti označeni test do
py.test -m <name> -m <name> mentions the marker name
Pokrenite py.test -m set1. Ovo će pokrenuti metode test_file1_method1, test_file2_method1, test_file2_method2.
Pokretanje py.test -m set2 pokrenut će test_file1_method2.
Pokrenite testove paralelno s Pytestom
Obično će testni paket imati višestruke testne datoteke i stotine testnih metoda za čije će izvršenje biti potrebno dosta vremena. Pytest nam omogućuje paralelno pokretanje testova.
Za to moramo prvo instalirati pytest-xdist pokretanjem
pip install pytest-xdist
Sada možete pokrenuti testove do
py.test -n 4
-n izvodi testove koristeći više radnika. U gornjoj naredbi bit će 4 radnika koji će izvoditi test.
Pytest rasporedi
Fiksture se koriste kada želimo pokrenuti neki kod prije svake metode testiranja. Dakle, umjesto ponavljanja istog koda u svakom testu, mi definiramo rasporede. Obično se fixtures koriste za inicijalizaciju veza baze podataka, prosljeđivanje baze, itd
Metoda je označena kao Pytest fixture označavanjem sa
@pytest.fixture
Metoda ispitivanja može koristiti Pytest učvršćenje spominjanjem učvršćenja kao ulaznog parametra.
Napravite novu datoteku test_basic_fixture.py sa sljedećim kodom
import pytest @pytest.fixture def supply_AA_BB_CC(): aa=25 bb =35 cc=45 return [aa,bb,cc] def test_comparewithAA(supply_AA_BB_CC): zz=35 assert supply_AA_BB_CC[0]==zz,"aa and zz comparison failed" def test_comparewithBB(supply_AA_BB_CC): zz=35 assert supply_AA_BB_CC[1]==zz,"bb and zz comparison failed" def test_comparewithCC(supply_AA_BB_CC): zz=35 assert supply_AA_BB_CC[2]==zz,"cc and zz comparison failed"
Ovdje
- Imamo učvršćenje pod nazivom supply_AA_BB_CC. Ova metoda će vratiti popis od 3 vrijednosti.
- Imamo 3 ispitne metode za usporedbu svake vrijednosti.
Svaka od testnih funkcija ima ulazni argument čije se ime podudara s dostupnom postavkom. Pytest zatim poziva odgovarajuću metodu fixture i vraćene vrijednosti će biti pohranjene u ulaznom argumentu, ovdje popis [25,35,45]. Sada se stavke popisa koriste u testnim metodama za usporedbu.
Sada pokrenite test i pogledajte rezultat
py.test test_basic_fixture
test_basic_fixture.py::test_comparewithAA FAILED
test_basic_fixture.py::test_comparewithBB PASSED
test_basic_fixture.py::test_comparewithCC FAILED
============================================== FAILURES ==============================================
_________________________________________ test_comparewithAA _________________________________________
supply_AA_BB_CC = [25, 35, 45]
def test_comparewithAA(supply_AA_BB_CC):
zz=35
> assert supply_AA_BB_CC[0]==zz,"aa and zz comparison failed"
E AssertionError: aa and zz comparison failed
E assert 25 == 35
test_basic_fixture.py:10: AssertionError
_________________________________________ test_comparewithCC _________________________________________
supply_AA_BB_CC = [25, 35, 45]
def test_comparewithCC(supply_AA_BB_CC):
zz=35
> assert supply_AA_BB_CC[2]==zz,"cc and zz comparison failed"
E AssertionError: cc and zz comparison failed
E assert 45 == 35
test_basic_fixture.py:16: AssertionError
================================= 2 failed, 1 passed in 0.05 seconds =================================
Test test_comparewithBB je prošao jer je zz=BB=35, a preostala 2 testa nisu uspjela.
Metoda fixture ima opseg samo unutar te testne datoteke u kojoj je definirana. Ako pokušamo pristupiti učvršćenju u nekoj drugoj testnoj datoteci, dobit ćemo pogrešku koja kaže učvršćenje 'supply_AA_BB_CC' nije pronađen za metode ispitivanja u drugim datotekama.
Da bismo koristili isti fixture protiv više testnih datoteka, stvorit ćemo metode fixture u datoteci pod nazivom conftest.py.
Pogledajmo to na donjem primjeru PyTest-a. Napravite 3 datoteke conftest.py, test_basic_fixture.py, test_basic_fixture2.py sa sljedećim kodom
conftest.py
import pytest @pytest.fixture def supply_AA_BB_CC(): aa=25 bb =35 cc=45 return [aa,bb,cc]
test_basic_fixture.py
import pytest def test_comparewithAA(supply_AA_BB_CC): zz=35 assert supply_AA_BB_CC[0]==zz,"aa and zz comparison failed" def test_comparewithBB(supply_AA_BB_CC): zz=35 assert supply_AA_BB_CC[1]==zz,"bb and zz comparison failed" def test_comparewithCC(supply_AA_BB_CC): zz=35 assert supply_AA_BB_CC[2]==zz,"cc and zz comparison failed"
test_basic_fixture2.py
import pytest def test_comparewithAA_file2(supply_AA_BB_CC): zz=25 assert supply_AA_BB_CC[0]==zz,"aa and zz comparison failed" def test_comparewithBB_file2(supply_AA_BB_CC): zz=25 assert supply_AA_BB_CC[1]==zz,"bb and zz comparison failed" def test_comparewithCC_file2(supply_AA_BB_CC): zz=25 assert supply_AA_BB_CC[2]==zz,"cc and zz comparison failed"
pytest će prvo potražiti fixture u testnoj datoteci, a ako je ne pronađe, tražit će u conftest.py
Pokrenite test pomoću py.test -k test_comparewith -v da biste dobili rezultat kao u nastavku
test_basic_fixture.py::test_comparewithAA FAILED test_basic_fixture.py::test_comparewithBB PASSED test_basic_fixture.py::test_comparewithCC FAILED test_basic_fixture2.py::test_comparewithAA_file2 PASSED test_basic_fixture2.py::test_comparewithBB_file2 FAILED test_basic_fixture2.py::test_comparewithCC_file2 FAILED
Pytest parametrizirani test
Svrha parametriziranja testa je pokretanje testa protiv više skupova argumenata. To možemo učiniti pomoću @pytest.mark.parametrize.
To ćemo vidjeti u donjem primjeru PyTest-a. Ovdje ćemo proslijediti 3 argumenta testnoj metodi. Ova testna metoda će dodati prva 2 argumenta i usporediti ih s 3. argumentom.
Stvorite testnu datoteku test_addition.py s donjim kodom
import pytest
@pytest.mark.parametrize("input1, input2, output",[(5,5,10),(3,5,12)])
def test_add(input1, input2, output):
assert input1+input2 == output,"failed"
Ovdje ispitna metoda prihvaća 3 argumenta - ulaz1, ulaz2, izlaz. Dodaje input1 i input2 i uspoređuje s izlazom.
Pokrenimo test pomoću py.test -k test_add -v i vidimo rezultat
test_addition.py::test_add[5-5-10] PASSED
test_addition.py::test_add[3-5-12] FAILED
============================================== FAILURES ==============================================
__________________________________________ test_add[3-5-12] __________________________________________
input1 = 3, input2 = 5, output = 12
@pytest.mark.parametrize("input1, input2, output",[(5,5,10),(3,5,12)])
def test_add(input1, input2, output):
> assert input1+input2 == output,"failed"
E AssertionError: failed
E assert (3 + 5) == 12
test_addition.py:5: AssertionError
Možete vidjeti da su se testovi izvodili 2 puta – jedan provjerava 5+5 ==10, a drugi provjerava 3+5 ==12
test_addition.py::test_add[5-5-10] PROŠAO
test_addition.py::test_add[3-5-12] NIJE USPJELO
Pytest Xfail / Preskoči testove
Bit će nekih situacija u kojima ne želimo izvršiti test, ili a testni slučaj nije relevantno za određeno vrijeme. U tim situacijama imamo opciju Xfail test ili preskočiti testove
Xfailed test će se izvršiti, ali se neće računati kao djelomično neuspjeli ili prošli testovi. Neće biti prikazano praćenje ako taj test ne uspije. Možemo xfail testove koristeći
@pytest.mark.xfail.
Preskakanje testa znači da se test neće izvršiti. Možemo preskočiti testove pomoću
@pytest.mark.skip.
Uredite test_addition.py pomoću donjeg koda
import pytest @pytest.mark.skip def test_add_1(): assert 100+200 == 400,"failed" @pytest.mark.skip def test_add_2(): assert 100+200 == 300,"failed" @pytest.mark.xfail def test_add_3(): assert 15+13 == 28,"failed" @pytest.mark.xfail def test_add_4(): assert 15+13 == 100,"failed" def test_add_5(): assert 3+2 == 5,"failed" def test_add_6(): assert 3+2 == 6,"failed"
Ovdje
- test_add_1 i test_add_2 su preskočeni i neće se izvršiti.
- test_add_3 i test_add_4 su xfailed. Ovi testovi će se izvršiti i bit će dio testova xfailed(na testu neuspješno) ili xpassed(na test passed). Neće biti povratnog traga za kvarove.
- test_add_5 i test_add_6 će se izvršiti, a test_add_6 će prijaviti neuspjeh s praćenjem dok test_add_5 prolazi
Izvršite test pomoću py.test test_addition.py -v i pogledajte rezultat
test_addition.py::test_add_1 SKIPPED
test_addition.py::test_add_2 SKIPPED
test_addition.py::test_add_3 XPASS
test_addition.py::test_add_4 xfail
test_addition.py::test_add_5 PASSED
test_addition.py::test_add_6 FAILED
============================================== FAILURES ==============================================
_____________________________________________ test_add_6 _____________________________________________
def test_add_6():
> assert 3+2 == 6,"failed"
E AssertionError: failed
E assert (3 + 2) == 6
test_addition.py:24: AssertionError
================ 1 failed, 1 passed, 2 skipped, 1 xfailed, 1 xpassed in 0.07 seconds =================
XML rezultata
Možemo stvoriti rezultate testa u XML formatu koje možemo poslati poslužiteljima kontinuirane integracije za daljnju obradu i tako. To se može učiniti pomoću
py.test test_sample1.py -v –junitxml=”result.xml”
Rezultat.xml će zabilježiti rezultat izvršenja testa. U nastavku pronađite primjer rezultata.xml
<?xml version="1.0" encoding="UTF-8"?>
<testsuite errors="0" failures="1" name="pytest" skips="0" tests="2" time="0.046">
<testcase classname="test_sample1" file="test_sample1.py" line="3" name="test_file1_method1" time="0.001384973526">
<failure message="AssertionError:test failed because x=5 y=6 assert 5 ==6">
@pytest.mark.set1
def test_file1_method1():
x=5
y=6
assert x+1 == y,"test failed"
> assert x == y,"test failed because x=" + str(x) + " y=" + str(y)
E AssertionError: test failed because x=5 y=6
E assert 5 == 6
test_sample1.py:9: AssertionError
</failure>
</testcase>
<testcase classname="test_sample1" file="test_sample1.py" line="10" name="test_file1_method2" time="0.000830173492432" />
</testsuite>
Iz možemo vidjeti ukupno dva testa od kojih je jedan pao. Ispod toga možete vidjeti pojedinosti o svakom izvršenom testu pod označiti.
Pytest Framework Testiranje API-ja
Sada ćemo stvoriti mali okvir pytest za testiranje API-ja. API koji se ovdje koristi je besplatan od https://reqres.in/. Ova web stranica služi samo za pružanje testiranog API-ja. Ova web stranica ne pohranjuje naše podatke.
Ovdje ćemo napisati neke testove za
- navodeći neke korisnike
- prijava s korisnicima
Stvorite donje datoteke s danim kodom
conftest.py – ima uređaj koji će dostaviti osnovni url za sve testne metode
import pytest @pytest.fixture def supply_url(): return "https://reqres.in/api"
test_list_user.py – sadrži testne metode za ispisivanje valjanih i nevažećih korisnika
- test_list_valid_user testira ispravno dohvaćanje korisnika i provjerava odgovor
- test_list_invaliduser testira dohvaćanje nevažećih korisnika i provjerava odgovor
import pytest
import requests
import json
@pytest.mark.parametrize("userid, firstname",[(1,"George"),(2,"Janet")])
def test_list_valid_user(supply_url,userid,firstname):
url = supply_url + "/users/" + str(userid)
resp = requests.get(url)
j = json.loads(resp.text)
assert resp.status_code == 200, resp.text
assert j['data']['id'] == userid, resp.text
assert j['data']['first_name'] == firstname, resp.text
def test_list_invaliduser(supply_url):
url = supply_url + "/users/50"
resp = requests.get(url)
assert resp.status_code == 404, resp.text
test_login_user.py – sadrži testne metode za testiranje funkcionalnosti prijave.
- test_login_valid testira ispravan pokušaj prijave s e-poštom i lozinkom
- test_login_no_password testira nevažeći pokušaj prijave bez dodavanja lozinke
- test_login_no_email testira nevažeći pokušaj prijave bez prosljeđivanja e-pošte.
import pytest
import requests
import json
def test_login_valid(supply_url):
url = supply_url + "/login/"
data = {'email':'[email protected]','password':'something'}
resp = requests.post(url, data=data)
j = json.loads(resp.text)
assert resp.status_code == 200, resp.text
assert j['token'] == "QpwL5tke4Pnpja7X", resp.text
def test_login_no_password(supply_url):
url = supply_url + "/login/"
data = {'email':'[email protected]'}
resp = requests.post(url, data=data)
j = json.loads(resp.text)
assert resp.status_code == 400, resp.text
assert j['error'] == "Missing password", resp.text
def test_login_no_email(supply_url):
url = supply_url + "/login/"
data = {}
resp = requests.post(url, data=data)
j = json.loads(resp.text)
assert resp.status_code == 400, resp.text
assert j['error'] == "Missing email or username", resp.text
Pokrenite test koristeći py.test -v
Pogledajte rezultat kao
test_list_user.py::test_list_valid_user[1-George] PASSED test_list_user.py::test_list_valid_user[2-Janet] PASSED test_list_user.py::test_list_invaliduser PASSED test_login_user.py::test_login_valid PASSED test_login_user.py::test_login_no_password PASSED test_login_user.py::test_login_no_email PASSED
Ažurirajte testove i isprobajte različite rezultate
Rezime
U ovom smo vodiču za PyTest pokrili
- Instalirajte pytest pomoću pip instalirati pytest=2.9.1
- Jednostavan program pytest i pokrenite ga naredbom py.test.
- Izjave tvrdnji, assert x==y, vratit će ili True ili False.
- Kako pytest identificira testne datoteke i metode.
- Testne datoteke koje počinju s test_ ili završava sa _test
- Metode ispitivanja počevši od test
- naredba py.test pokrenut će sve testne datoteke u toj mapi i podmapama. Za pokretanje određene datoteke možemo koristiti naredbu py.test
- Pokrenite podskup testnih metoda
- Grupiranje naziva testova prema podnizu matching.py.test -k -v će pokrenuti sve testove koji imaju u svoje ime.
- Pokreni test pomoću markera. Označite testove koristeći @pytest.mark. i pokrenite testove koristeći pytest -m za pokretanje testova označenih kao .
- Pokrenite testove paralelno
- Instalirajte pytest-xdist koristeći pip install pytest-xdist
- Pokrenite testove koristeći py.test -n NUM gdje je NUM broj radnika
- Stvaranje metoda fixture za pokretanje koda prije svakog testa označavanjem metode s @pytest.fixture
- Opseg metode fixture je unutar datoteke koja je definirana.
- Metodi fixture može se pristupiti preko više testnih datoteka definiranjem u datoteci conftest.py.
- Testna metoda može pristupiti Pytest fixture koristeći je kao ulazni argument.
- Testovi parametriranja za izvođenje na više skupova ulaza.
@pytest.mark.parametrize(“ulaz1, ulaz2, izlaz”,[(5,5,10),(3,5,12)])
def test_add(ulaz1, ulaz2, izlaz):
potvrdi input1+input2 == output,”failed”
pokrenut će test s ulazima (5,5,10) i (3,5,12) - Skip/xfail testovi koristeći @pytets.mark.skip i @pytest.mark.xfail
- Stvorite rezultate testa u XML formatu koji pokriva pojedinosti o izvršenom testu koristeći py.test test_sample1.py -v –junitxml=”result.xml”
- Uzorak okvira pytest za testiranje API-ja




