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

installera PyTest

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

Första Basic PyTest

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

Första Basic PyTest

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

Kör tester parallellt med Pytest

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