PyTest Tutorial: Vad är, hur man installerar, ramverk, påståenden
Vad är PyTest?
PyTest är ett testramverk som tillåter användare att skriva testkoder med hjälp av Python programmeringsspråk. Det hjälper dig att skriva enkla och skalbara testfall för databaser, API:er eller UI. PyTest används främst för att skriva tester för API:er. Det hjälper till att skriva tester från enkla enhetstester till komplexa funktionstester.
Varför använda PyTest?
Några av fördelarna med pytest är
- Mycket lätt att börja med på grund av dess enkla och lätta syntax.
- Kan köra tester parallellt.
- Kan köra ett specifikt test eller en delmängd av tester
- Upptäck automatiskt tester
- Hoppa över tester
- Öppen källa
Hur man installerar PyTest
Följande är en process för hur man installerar PyTest:
Steg 1) Du kan installera pytest genom att
pip install pytest==2.9.1
När installationen är klar kan du bekräfta den med av
py.test -h
Detta visar hjälpen
Första Basic PyTest
Nu kommer vi att lära oss hur man använder Pytest med ett grundläggande PyTest-exempel.
Skapa en mapp study_pytest. Vi kommer att skapa våra testfiler i den här mappen.
Vänligen navigera till den mappen på din kommandorad.
Skapa en fil med namnet test_sample1.py i mappen
Lägg till koden nedan i den och spara
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 testet med kommandot
py.test
Du får utdata 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
Här i test_sample1.py F.
F säger misslyckande
Punkt(.) säger framgång.
I felavsnittet kan du se de misslyckade metoderna och felraden. Här betyder x==y 5==6 vilket är falskt.
Nästa i denna PyTest-handledning kommer vi att lära oss om påstående i PyTest.
Påståenden i PyTest
Pytest-påståenden är kontroller som returnerar antingen True eller False status. I Python Pytest, om ett påstående misslyckas i en testmetod, stoppas den metodexekveringen där. Den återstående koden i den testmetoden exekveras inte, och Pytest-påståenden kommer att fortsätta med nästa testmetod.
Pytest Assert exempel:
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.
Tänk
assert x == y,"test failed because x=" + str(x) + " y=" + str(y)
Placera den här koden i test_file1_method1() istället för påståendet
assert x == y,"test failed"
Att köra testet ger felet som AssertionError: testet misslyckades x=5 y=6
Hur PyTest identifierar testfilerna och testmetoderna
Som standard identifierar pytest endast filnamnen som börjar med testa_ eller slutar med _testa som testfiler. Vi kan dock uttryckligen nämna andra filnamn (förklaras senare). Pytest kräver testmetodnamnen till att börja med "testa.” Alla andra metodnamn kommer att ignoreras även om vi uttryckligen ber om att köra dessa metoder.
Se några exempel på giltiga och ogiltiga pytest-filnamn
test_login.py - valid login_test.py - valid testlogin.py -invalid logintest.py -invalid
Obs: Ja, vi kan uttryckligen be pytest att välja testlogin.py och logintest.py
Se några exempel på giltiga och ogiltiga pytest-testmetoder
def test_file1_method1(): - valid def testfile1_method1(): - valid def file1_method1(): - invalid
Obs: Även om vi uttryckligen nämner file1_method1() kommer pytest inte att köra den här metoden.
Kör flera tester från en specifik fil och flera filer
För närvarande, inne i mappen study_pytest, har vi filen test_sample1.py. Anta att vi har flera filer, säg test_sample2.py, test_sample3.py. För att köra alla tester från alla filer i mappen och undermappar behöver vi bara köra kommandot pytest.
py.test
Detta kommer att köra alla filnamn som börjar med test_ och filnamnen som slutar med _test i den mappen och undermappar under den mappen.
För att köra tester endast från en specifik fil kan vi använda py.test
py.test test_sample1.py
Kör en delmängd av hela testet med PyTest
Ibland vill vi inte köra hela testsviten. Pytest låter oss köra specifika tester. Vi kan göra det på 2 sätt
- Gruppering av testnamn efter delsträngsmatchning
- Gruppering av tester efter markörer
Vi har redan test_sample1.py. Skapa en fil test_sample2.py och lägg till koden nedan 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å har vi för närvarande
• test_sample1.py • test_file1_method1() • test_file1_method2() • test_sample2.py • test_file2_method1() • test_file2_method2()
Alternativ 1) Kör tester genom delsträngsmatchning
Här för att köra alla tester med metod1 i sitt namn måste vi köra
py.test -k method1 -v -k <expression> is used to represent the substring to match -v increases the verbosity
Så att köra py.test -k method1 -v kommer att ge dig följande 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 ===============================
Här kan du se mot slutet 2 tester avmarkerade av '-kmethod1' som är test_fil1_metod2 och test_fil2_metod2
Prova att springa med olika 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'
Alternativ 2) Kör tester med markörer
Pytest låter oss ställa in olika attribut för testmetoderna med hjälp av pytest-markörer, @pytest.mark . För att använda markörer i testfilen måste vi importera pytest på testfilerna.
Här kommer vi att tillämpa olika markörnamn på testmetoder och köra specifika tester baserade på markörnamn. Vi kan definiera markörerna på varje testnamn genom att använda
@pytest.mark.<name>.
Vi definierar markörer set1 och set2 på testmetoderna, och vi kommer att köra testet med markörnamnen. Uppdatera testfilerna med följande kod
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öra det markerade testet av
py.test -m <name> -m <name> mentions the marker name
Kör py.test -m set1. Detta kommer att köra metoderna test_file1_metod1, test_fil2_metod1, test_fil2_metod2.
Att köra py.test -m set2 kommer att köra test_fil1_metod2.
Kör tester parallellt med Pytest
Vanligtvis kommer en testsvit att ha flera testfiler och hundratals testmetoder som kommer att ta avsevärd tid att utföra. Pytest låter oss köra tester parallellt.
För det måste vi först installera pytest-xdist genom att köra
pip install pytest-xdist
Du kan köra tester nu genom att
py.test -n 4
-n kör testerna genom att använda flera arbetare. I kommandot ovan kommer det att finnas 4 arbetare för att köra testet.
Pytest-fixturer
Fixturer används när vi vill köra lite kod före varje testmetod. Så istället för att upprepa samma kod i varje test definierar vi fixturer. Vanligtvis används fixturer för att initiera databasanslutningar, skicka basen etc
En metod markeras som en Pytest-fixtur genom att markera med
@pytest.fixture
En testmetod kan använda en Pytest-fixtur genom att nämna fixturen som en indataparameter.
Skapa en ny fil test_basic_fixture.py med följande kod
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"
Här
- Vi har en fixtur som heter supply_AA_BB_CC. Denna metod returnerar en lista med 3 värden.
- Vi har 3 testmetoder som jämför mot vart och ett av värdena.
Var och en av testfunktionerna har ett inmatningsargument vars namn matchar en tillgänglig fixtur. Pytest anropar sedan motsvarande fixturmetod och de returnerade värdena kommer att lagras i inmatningsargumentet , här listan [25,35,45]. Nu används listposterna i testmetoder för jämförelsen.
Kör nu testet och 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 =================================
Testet test_comparewithBB är godkänt eftersom zz=BB=35, och de återstående 2 testerna är underkända.
Fixturmetoden har en räckvidd endast inom den testfil som den är definierad. Om vi försöker komma åt fixturen i någon annan testfil får vi ett felmeddelande som säger fixtur 'supply_AA_BB_CC' hittades inte för testmetoderna i andra filer.
För att använda samma fixtur mot flera testfiler kommer vi att skapa fixturmetoder i en fil som heter conftest.py.
Låt oss se detta genom PyTest-exemplet nedan. Skapa 3 filer conftest.py, test_basic_fixture.py, test_basic_fixture2.py med följande kod
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 kommer att leta efter fixturen i testfilen först och om den inte hittas kommer den att leta i conftest.py
Kör testet med py.test -k test_comparewith -v för att få resultatet enligt nedan
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 Parameteriserat test
Syftet med att parametrisera ett test är att köra ett test mot flera uppsättningar av argument. Vi kan göra detta genom @pytest.mark.parametrize.
Vi kommer att se detta med PyTest-exemplet nedan. Här kommer vi att skicka 3 argument till en testmetod. Denna testmetod kommer att lägga till de två första argumenten och jämföra dem med det tredje argumentet.
Skapa testfilen test_addition.py med koden nedan
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"
Här accepterar testmetoden 3 argument - input1, input2, output. Den lägger till input1 och input2 och jämför med output.
Låt oss köra testet med py.test -k test_add -v och 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 att testerna kördes 2 gånger – ett kontrollerade 5+5 ==10 och det andra kontrollerade 3+5 ==12
test_addition.py::test_add[5-5-10] godkänd
test_addition.py::test_add[3-5-12] MISSLYCKADES
Pytest Xfail / Hoppa över tester
Det kommer att finnas vissa situationer där vi inte vill utföra ett test, eller en testfall är inte relevant för en viss tid. I dessa situationer har vi möjlighet att Xmisslyckas testet eller hoppa över testerna
Det xmisslyckade testet kommer att köras, men det kommer inte att räknas som delvis underkänt eller godkänt test. Det kommer inte att visas någon spårning om testet misslyckas. Vi kan misslyckas tester med
@pytest.mark.xfail.
Att hoppa över ett test innebär att testet inte kommer att utföras. Vi kan hoppa över tester med hjälp av
@pytest.mark.hoppa över.
Redigera test_addition.py med koden nedan
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"
Här
- test_add_1 och test_add_2 hoppas över och kommer inte att köras.
- test_add_3 och test_add_4 misslyckades. Dessa tester kommer att utföras och kommer att ingå i xmisslyckade (vid test misslyckande) eller xgodkända (vid test godkänd). Det kommer inte att finnas någon spårning för misslyckanden.
- test_add_5 och test_add_6 kommer att köras och test_add_6 kommer att rapportera fel med spårning medan test_add_5 klarar
Utför testet med py.test test_addition.py -v och 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 =================
Resultat XML
Vi kan skapa testresultat i XML-format som vi kan mata till Continuous Integration-servrar för vidare bearbetning och så. Detta kan göras av
py.test test_sample1.py -v –junitxml="result.xml"
Resultatet.xml kommer att registrera testkörningsresultatet. Hitta ett exempel på result.xml nedan
<?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>
Från vi kan se totalt två test varav ett är underkänt. Nedan kan du se detaljer om varje utfört test under märka.
Pytest Framework Testa ett API
Nu ska vi skapa ett litet pytest-ramverk för att testa ett API. API:et som används här är gratis från https://reqres.in/. Denna webbplats är bara för att tillhandahålla testbart API. Denna webbplats lagrar inte vår data.
Här kommer vi att skriva några tester för
- listar några användare
- logga in med användare
Skapa nedanstående filer med den angivna koden
conftest.py – ha en fixtur som tillhandahåller bas-url för alla testmetoder
import pytest @pytest.fixture def supply_url(): return "https://reqres.in/api"
test_list_user.py – innehåller testmetoderna för att lista giltiga och ogiltiga användare
- test_list_valid_user testar för giltig användarhämtning och verifierar svaret
- test_list_invaliduser testar ogiltig användarhämtning och verifierar 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 – innehåller testmetoder för att testa inloggningsfunktioner.
- test_login_valid testar det giltiga inloggningsförsöket med e-post och lösenord
- test_login_no_password testar det ogiltiga inloggningsförsöket utan att skicka lösenordet
- test_login_no_email testar det ogiltiga inloggningsförsöket utan att skicka e-post.
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 testet med 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
Uppdatera testerna och prova olika utgångar
Sammanfattning
I denna PyTest-handledning täckte vi
- Installera pytest med pip installera pytest=2.9.1
- Enkelt pytest-program och kör det med kommandot py.test.
- Påstående uttalanden, hävda x==y, returnerar antingen True eller False.
- Hur pytest identifierar testfiler och metoder.
- Testfiler som börjar med testa_ eller slutar med _testa
- Testmetoder som börjar med test
- kommandot py.test kommer att köra alla testfiler i den mappen och undermappar. För att köra en specifik fil kan vi använda kommandot py.test
- Kör en delmängd av testmetoder
- Gruppering av testnamn efter delsträng matching.py.test -k -v kommer att köra alla tester som har i dess namn.
- Kör test med markörer. Markera testerna med @pytest.mark. och kör testerna med pytest -m att köra tester markerade som .
- Kör tester parallellt
- Installera pytest-xdist med pip install pytest-xdist
- Kör tester med py.test -n NUM där NUM är antalet arbetare
- Skapa fixturmetoder för att köra kod före varje test genom att markera metoden med @pytest.fixture
- Omfattningen av en fixturmetod ligger inom den fil som den definieras.
- En fixturmetod kan nås över flera testfiler genom att definiera den i filen conftest.py.
- En testmetod kan komma åt en Pytest-fixtur genom att använda den som ett inmatningsargument.
- Parametriserar tester för att köra den mot flera uppsättningar ingångar.
@pytest.mark.parametrize(“input1, input2, output”,[(5,5,10),(3,5,12)])
def test_add(ingång1, input2, output):
hävda ingång1+ingång2 == utgång,”misslyckades”
kommer att köra testet med ingångar (5,5,10) och (3,5,12) - Hoppa över/xfail tester med @pytets.mark.skip och @pytest.mark.xfail
- Skapa testresultat i XML-format som täcker utförda testdetaljer med py.test test_sample1.py -v –junitxml=”result.xml”
- Ett exempel på pytest-ramverk för att testa ett API