PyTest Tutorial: Hvad er, hvordan man installerer, rammeværk, påstande
Hvad er PyTest?
PyTest er en testramme, der giver brugerne mulighed for at skrive testkoder vha Python programmeringssprog. Det hjælper dig med at skrive enkle og skalerbare testcases til databaser, API'er eller UI. PyTest bruges hovedsageligt til at skrive test til API'er. Det hjælper med at skrive test fra simple enhedstests til komplekse funktionelle tests.
Hvorfor bruge PyTest?
Nogle af fordelene ved pytest er
- Meget let at starte med på grund af dens enkle og nemme syntaks.
- Kan køre test parallelt.
- Kan køre en specifik test eller en delmængde af tests
- Registrer automatisk tests
- Spring prøver over
- Open source
Sådan installeres PyTest
Følgende er en proces til, hvordan du installerer PyTest:
Trin 1) Du kan installere pytest ved
pip install pytest==2.9.1
Når installationen er færdig, kan du bekræfte den med ved
py.test -h
Dette vil vise hjælpen
Første grundlæggende PyTest
Nu vil vi lære, hvordan man bruger Pytest med et grundlæggende PyTest-eksempel.
Opret en mappe study_pytest. Vi vil oprette vores testfiler i denne mappe.
Venligst naviger til den mappe på din kommandolinje.
Opret en fil med navnet test_sample1.py inde i mappen
Tilføj nedenstående kode til det og gem
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"
Kør testen ved hjælp af kommandoen
py.test
Du får output som
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
Her i test_sample1.py F.
F siger fiasko
Prik(.) siger succes.
I fejlafsnittet kan du se de(n) mislykkede metode(r) og fejllinjen. Her betyder x==y 5==6, hvilket er falsk.
Næste i denne PyTest-tutorial lærer vi om påstand i PyTest.
Påstande i PyTest
Pytest-påstande er kontroller, der returnerer enten True eller False status. I Python Pytest, hvis en påstand mislykkes i en testmetode, stoppes udførelse af denne metode der. Den resterende kode i den testmetode udføres ikke, og Pytest-påstande fortsætter med den næste testmetode.
Pytest Assert eksempler:
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.
Overvej
assert x == y,"test failed because x=" + str(x) + " y=" + str(y)
Placer denne kode i test_file1_method1() i stedet for påstanden
assert x == y,"test failed"
At køre testen vil give fejlen som AssertionError: Test mislykkedes x=5 y=6
Hvordan PyTest identificerer testfilerne og testmetoderne
Som standard identificerer pytest kun filnavnene, der starter med prøve_ eller slutter med _prøve som testfilerne. Vi kan dog udtrykkeligt nævne andre filnavne (forklaret senere). Pytest kræver testmetodenavnene til at starte med "prøve." Alle andre metodenavne vil blive ignoreret, selvom vi udtrykkeligt beder om at køre disse metoder.
Se nogle eksempler på gyldige og ugyldige pytest-filnavne
test_login.py - valid login_test.py - valid testlogin.py -invalid logintest.py -invalid
Bemærk: Ja, vi kan udtrykkeligt bede pytest om at vælge testlogin.py og logintest.py
Se nogle eksempler på gyldige og ugyldige pytest-testmetoder
def test_file1_method1(): - valid def testfile1_method1(): - valid def file1_method1(): - invalid
Bemærk: Selvom vi udtrykkeligt nævner file1_method1() vil pytest ikke køre denne metode.
Kør flere tests fra en specifik fil og flere filer
I øjeblikket, inde i mappen study_pytest, har vi en fil test_sample1.py. Antag, at vi har flere filer, f.eks. test_sample2.py, test_sample3.py. For at køre alle testene fra alle filerne i mappen og undermapperne skal vi bare køre pytest-kommandoen.
py.test
Dette vil køre alle filnavne, der starter med test_ og filnavne, der slutter med _test i den mappe og undermapper under den mappe.
For kun at køre test fra en bestemt fil, kan vi bruge py.test
py.test test_sample1.py
Kør et undersæt af hele testen med PyTest
Nogle gange ønsker vi ikke at køre hele testpakken. Pytest giver os mulighed for at køre specifikke tests. Vi kan gøre det på 2 måder
- Gruppering af testnavne efter understrengmatching
- Gruppering af test efter markører
Vi har allerede test_sample1.py. Opret en fil test_sample2.py og tilføj nedenstående kode i den
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"
Så det har vi pt
• test_sample1.py • test_file1_method1() • test_file1_method2() • test_sample2.py • test_file2_method1() • test_file2_method2()
Mulighed 1) Kør test ved at matche understrenge
Her skal vi køre alle de test, der har metode1 i sit navn
py.test -k method1 -v -k <expression> is used to represent the substring to match -v increases the verbosity
Så at køre py.test -k method1 -v vil give dig følgende resultat
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 ===============================
Her kan du se mod slutningen 2 test fravalgt af '-kmethod1' som er test_fil1_metode2 og test_fil2_metode2
Prøv at løbe med forskellige kombinationer som:-
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'
Mulighed 2) Kør test med markører
Pytest giver os mulighed for at indstille forskellige attributter for testmetoderne ved hjælp af pytest-markører, @pytest.mark . For at bruge markører i testfilen skal vi importere pytest på testfilerne.
Her vil vi anvende forskellige markørnavne på testmetoder og køre specifikke test baseret på markørnavne. Vi kan definere markørerne på hver testnavne ved at bruge
@pytest.mark.<name>.
Vi definerer markører sæt1 og sæt2 på testmetoderne, og vi vil køre testen ved at bruge markørnavnene. Opdater testfilerne med følgende kode
test_sample1.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_sample2.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"
Vi kan køre den markerede test ved
py.test -m <name> -m <name> mentions the marker name
Kør py.test -m set1. Dette vil køre metoderne test_file1_method1, test_file2_method1, test_file2_method2.
At køre py.test -m set2 vil køre test_file1_method2.
Kør tests parallelt med Pytest
Normalt vil en testpakke have flere testfiler og hundredvis af testmetoder, som vil tage lang tid at udføre. Pytest giver os mulighed for at køre tests parallelt.
Til det skal vi først installere pytest-xdist ved at køre
pip install pytest-xdist
Du kan køre test nu ved
py.test -n 4
-n kører testene ved at bruge flere arbejdere. I ovenstående kommando vil der være 4 arbejdere til at køre testen.
Pytest-armaturer
Fixtures bruges, når vi ønsker at køre noget kode før hver testmetode. Så i stedet for at gentage den samme kode i hver test, definerer vi armaturer. Normalt bruges fixtures til at initialisere databaseforbindelser, sende basen osv
En metode markeres som et Pytest-armatur ved at markere med
@pytest.fixture
En testmetode kan bruge en Pytest-fixtur ved at nævne fixturen som en inputparameter.
Opret en ny fil test_basic_fixture.py med følgende kode
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"
Her
- Vi har et armatur ved navn supply_AA_BB_CC. Denne metode vil returnere en liste med 3 værdier.
- Vi har 3 testmetoder, der sammenligner med hver af værdierne.
Hver af testfunktionerne har et input-argument, hvis navn stemmer overens med en tilgængelig fixtur. Pytest kalder derefter den tilsvarende fixturmetode, og de returnerede værdier vil blive gemt i input-argumentet , her listen [25,35,45]. Nu bliver listepunkterne brugt i testmetoder til sammenligningen.
Kør nu testen og se resultatet
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 =================================
Testen test_comparewithBB er bestået, da zz=BB=35, og de resterende 2 tests er mislykkedes.
Fixturmetoden har kun et omfang inden for den testfil, den er defineret. Hvis vi forsøger at få adgang til armaturet i en anden testfil, får vi en fejl, der siger fixture 'supply_AA_BB_CC' ikke fundet for testmetoderne i andre filer.
For at bruge den samme fixtur mod flere testfiler, vil vi oprette fixturmetoder i en fil kaldet conftest.py.
Lad os se dette ved nedenstående PyTest-eksempel. Opret 3 filer conftest.py, test_basic_fixture.py, test_basic_fixture2.py med følgende kode
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 vil lede efter fixturen i testfilen først, og hvis den ikke findes, vil den søge i conftest.py
Kør testen med py.test -k test_comparewith -v for at få resultatet som nedenfor
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 parametriseret test
Formålet med at parametrere en test er at køre en test mod flere sæt argumenter. Vi kan gøre dette ved at @pytest.mark.parametrize.
Vi vil se dette med nedenstående PyTest-eksempel. Her vil vi videregive 3 argumenter til en testmetode. Denne testmetode vil tilføje de første 2 argumenter og sammenligne dem med det 3. argument.
Opret testfilen test_addition.py med nedenstående kode
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"
Her accepterer testmetoden 3 argumenter - input1, input2, output. Den tilføjer input1 og input2 og sammenligner med outputtet.
Lad os køre testen med py.test -k test_add -v og se resultatet
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
Du kan se testene kørt 2 gange – den ene tjekker 5+5 ==10 og den anden tjekker 3+5 ==12
test_addition.py::test_add[5-5-10] BESTÅET
test_addition.py::test_add[3-5-12] MISLYKKES
Pytest Xfail / Spring over tests
Der vil være nogle situationer, hvor vi ikke ønsker at udføre en test, eller en test sag er ikke relevant for et bestemt tidspunkt. I disse situationer har vi mulighed for at Xfail testen eller springe testene over
Den xmislykkede test vil blive udført, men den tælles ikke som delbeståede eller beståede prøver. Der vises ingen sporing, hvis testen mislykkes. Vi kan xfejle test vha
@pytest.mark.xfail.
At springe en test over betyder, at testen ikke bliver udført. Vi kan springe prøver over vha
@pytest.mark.spring.
Rediger test_addition.py med nedenstående kode
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"
Her
- test_add_1 og test_add_2 springes over og vil ikke blive udført.
- test_add_3 og test_add_4 er xfejlet. Disse tests vil blive udført og vil være en del af xmislykkede (ved testfejl) eller xbeståede (ved testbestået) test. Der vil ikke være nogen sporing for fejl.
- test_add_5 og test_add_6 vil blive udført, og test_add_6 vil rapportere fejl med sporing, mens test_add_5 består
Udfør testen ved at py.test test_addition.py -v og se resultatet
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 =================
Resultater XML
Vi kan oprette testresultater i XML-format, som vi kan føre til kontinuerlig integrationsservere til videre behandling og så. Dette kan gøres ved
py.test test_sample1.py -v –junitxml="result.xml"
Resultatet.xml registrerer testudførelsesresultatet. Find et eksempel på result.xml nedenfor
<?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>
Fra vi kan se i alt to tests, hvoraf den ene er ikke bestået. Nedenfor kan du se detaljerne vedrørende hver udført test under tag.
Pytest Framework Test af en API
Nu vil vi lave en lille pytest-ramme til at teste en API. Den her anvendte API er en gratis fra https://reqres.in/. Denne hjemmeside er kun for at levere testbar API. Denne hjemmeside gemmer ikke vores data.
Her vil vi skrive nogle tests til
- liste nogle brugere
- log ind med brugere
Opret nedenstående filer med den angivne kode
conftest.py – har en armatur, der leverer basis-url til alle testmetoder
import pytest @pytest.fixture def supply_url(): return "https://reqres.in/api"
test_list_user.py – indeholder testmetoderne til at angive gyldige og ugyldige brugere
- test_list_valid_user tester for gyldig brugerhentning og verificerer svaret
- test_list_invaliduser tester for ugyldig brugerhentning og verificerer svaret
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 – indeholder testmetoder til test af login-funktionalitet.
- test_login_valid tester det gyldige loginforsøg med e-mail og adgangskode
- test_login_no_password tester det ugyldige loginforsøg uden at sende adgangskode
- test_login_no_email tester det ugyldige loginforsøg uden at sende e-mail.
import pytest import requests import json def test_login_valid(supply_url): url = supply_url + "/login/" data = {'email':'test@test.com','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':'test@test.com'} 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
Kør testen ved at bruge py.test -v
Se resultatet som
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
Opdater testene og prøv forskellige udgange
Resumé
I denne PyTest-tutorial dækkede vi
- Installer pytest vha pip installation pytest=2.9.1
- Simpelt pytest-program og kør det med kommandoen py.test.
- Påstandsudsagn, assert x==y, vil returnere enten True eller False.
- Hvordan pytest identificerer testfiler og metoder.
- Test filer starter med prøve_ eller slutter med _prøve
- Testmetoder startende med prøve
- kommandoen py.test vil køre alle testfilerne i den mappe og undermapper. For at køre en bestemt fil kan vi bruge kommandoen py.test
- Kør et undersæt af testmetoder
- Gruppering af testnavne efter understreng matching.py.test -k -v vil køre alle de test, der har i dens navn.
- Kør test efter markører. Marker testene med @pytest.mark. og kør testene ved hjælp af pytest -m at køre test markeret som .
- Kør test parallelt
- Installer pytest-xdist ved hjælp af pip install pytest-xdist
- Kør test ved hjælp af py.test -n NUM hvor NUM er antallet af arbejdere
- Oprettelse af fixturmetoder til at køre kode før hver test ved at markere metoden med @pytest.fixture
- Omfanget af en fixturmetode er inden for den fil, den er defineret.
- En fixturmetode kan tilgås på tværs af flere testfiler ved at definere den i filen conftest.py.
- En testmetode kan få adgang til et Pytest-armatur ved at bruge det som et input-argument.
- Parametriserende test for at køre det mod flere sæt input.
@pytest.mark.parametrize(“input1, input2, output”,[(5,5,10),(3,5,12)])
def test_add(input1, input2, output):
hævde input1+input2 == output,"mislykkedes"
vil køre testen med input (5,5,10) og (3,5,12) - Spring/xfail tests ved hjælp af @pytets.mark.skip og @pytest.mark.xfail
- Opret testresultater i XML-format, som dækker udførte testdetaljer ved hjælp af py.test test_sample1.py -v –junitxml="result.xml"
- Et eksempel på en pytest-ramme til at teste en API