PyTest-veiledning: Hva er, hvordan installeres, rammeverk, påstander

Hva er PyTest?

PyTest er et testrammeverk som lar brukere skrive testkoder ved hjelp av Python programmeringsspråk. Det hjelper deg å skrive enkle og skalerbare testtilfeller for databaser, APIer eller brukergrensesnitt. PyTest brukes hovedsakelig til å skrive tester for APIer. Det hjelper å skrive tester fra enkle enhetstester til komplekse funksjonstester.

Hvorfor bruke PyTest?

Noen av fordelene med pytest er

  • Veldig lett å starte med på grunn av sin enkle og enkle syntaks.
  • Kan kjøre tester parallelt.
  • Kan kjøre en spesifikk test eller et undersett av tester
  • Oppdag tester automatisk
  • Hopp over tester
  • Åpen kilde

Hvordan installere PyTest

Følgende er en prosess for hvordan du installerer PyTest:

Trinn 1) Du kan installere pytest ved å

pip install pytest==2.9.1

Når installasjonen er fullført kan du bekrefte den med av

py.test -h

Dette vil vise hjelpen

installer PyTest

Første grunnleggende PyTest

Nå skal vi lære hvordan du bruker Pytest med et grunnleggende PyTest-eksempel.

Opprett en mappe study_pytest. Vi skal lage testfilene våre i denne mappen.

Naviger til den mappen på kommandolinjen.

Opprett en fil med navnet test_sample1.py inne i mappen

Første grunnleggende PyTest

Legg til koden nedenfor i den og lagre

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" 

Kjør testen ved å bruke kommandoen

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ørste grunnleggende PyTest

Her i test_sample1.py F.

F sier fiasko

Prikk(.) sier suksess.

I feildelen kan du se de(n) mislykkede metoden(e) og feillinjen. Her betyr x==y 5==6 som er usant.

Neste i denne PyTest-opplæringen vil vi lære om påstand i PyTest.

Påstander i PyTest

Pytest-påstander er sjekker som returnerer enten True eller False status. I Python Pytest, hvis en påstand mislykkes i en testmetode, stoppes kjøringen av denne metoden der. Den gjenværende koden i den testmetoden blir ikke utført, og Pytest-påstander vil fortsette med neste 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.

Vurder

assert x == y,"test failed because x=" + str(x) + " y=" + str(y)

Plasser denne koden i test_file1_method1() i stedet for påstanden

assert x == y,"test failed"

Å kjøre testen vil gi feilen som AssertionError: test mislyktes x=5 y=6

Hvordan PyTest identifiserer testfilene og testmetodene

Som standard identifiserer pytest bare filnavnene som begynner med test_ eller slutter med _test som testfilene. Vi kan imidlertid eksplisitt nevne andre filnavn (forklart senere). Pytest krever testmetodenavnene til å begynne med "test." Alle andre metodenavn vil bli ignorert selv om vi eksplisitt ber om å kjøre disse metodene.

Se noen eksempler på gyldige og ugyldige pytest-filnavn

test_login.py - valid
login_test.py - valid
testlogin.py -invalid
logintest.py -invalid

Merk: Ja, vi kan eksplisitt be pytest om å velge testlogin.py og logintest.py

Se noen eksempler på gyldige og ugyldige pytest-testmetoder

def test_file1_method1(): - valid
def testfile1_method1(): - valid
def file1_method1(): - invalid	

Merk: Selv om vi eksplisitt nevner file1_method1() vil ikke pytest kjøre denne metoden.

Kjør flere tester fra en bestemt fil og flere filer

For øyeblikket, inne i mappen study_pytest, har vi en fil test_sample1.py. Anta at vi har flere filer, si test_sample2.py, test_sample3.py. For å kjøre alle testene fra alle filene i mappen og undermappene trenger vi bare å kjøre pytest-kommandoen.

py.test

Dette vil kjøre alle filnavnene som starter med test_ og filnavnene som slutter med _test i den mappen og undermapper under den mappen.

For å kjøre tester kun fra en bestemt fil, kan vi bruke py.test

py.test test_sample1.py

Kjør et undersett av hele testen med PyTest

Noen ganger ønsker vi ikke å kjøre hele testpakken. Pytest lar oss kjøre spesifikke tester. Vi kan gjøre det på 2 måter

  • Gruppering av testnavn etter understrengmatching
  • Gruppering av tester etter markører

Vi har allerede test_sample1.py. Opprett en fil test_sample2.py og legg til koden nedenfor 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å vi har for tiden

• test_sample1.py
• test_file1_method1()
• test_file1_method2()
• test_sample2.py
• test_file2_method1()
• test_file2_method2()

Alternativ 1) Kjør tester ved å matche understrenger

Her for å kjøre alle testene med metode1 i navnet må vi kjøre

py.test -k method1 -v
-k <expression> is used to represent the substring to match
-v increases the verbosity

Så å kjøre py.test -k method1 -v vil gi deg 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 mot slutten 2 tester fravalgt av '-kmethod1' som er test_fil1_metode2 og testfil2_metode2

Prøv å løpe med forskjellige kombinasjoner 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) Kjør tester med markører

Pytest lar oss angi ulike attributter for testmetodene ved å bruke pytest-markører, @pytest.mark . For å bruke markører i testfilen, må vi importere pytest på testfilene.

Her vil vi bruke forskjellige markørnavn på testmetoder og kjøre spesifikke tester basert på markørnavn. Vi kan definere markørene på hvert testnavn ved å bruke

@pytest.mark.<name>.			

Vi definerer markører sett1 og sett2 på testmetodene, og vi vil kjøre testen ved å bruke markørnavnene. Oppdater testfilene 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 kjøre den merkede testen ved

py.test -m <name>
-m <name> mentions the marker name

Kjør py.test -m set1. Dette vil kjøre metodene test_file1_method1, test_file2_method1, test_file2_method2.

Å kjøre py.test -m set2 vil kjøre test_fil1_metode2.

Kjør tester parallelt med Pytest

Vanligvis vil en testpakke ha flere testfiler og hundrevis av testmetoder som vil ta lang tid å utføre. Pytest lar oss kjøre tester parallelt.

For det må vi først installere pytest-xdist ved å kjøre

pip install pytest-xdist

Kjør tester parallelt med Pytest

Du kan kjøre tester nå ved å

py.test -n 4

-n kjører testene ved å bruke flere arbeidere. I kommandoen ovenfor vil det være 4 arbeidere til å kjøre testen.

Pytest-armaturer

Fixtures brukes når vi ønsker å kjøre litt kode før hver testmetode. Så i stedet for å gjenta den samme koden i hver test, definerer vi inventar. Vanligvis brukes inventar for å initialisere databasetilkoblinger, sende basen osv

En metode merkes som en Pytest-fixtur ved å merke med

@pytest.fixture

En testmetode kan bruke en Pytest-fixtur ved å nevne fixturen som en inngangsparameter.

Opprett 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 en armatur som heter supply_AA_BB_CC. Denne metoden vil returnere en liste med 3 verdier.
  • Vi har 3 testmetoder som sammenligner med hver av verdiene.

Hver av testfunksjonene har et input-argument hvis navn samsvarer med en tilgjengelig fixtur. Pytest påkaller deretter den tilsvarende fixturmetoden og de returnerte verdiene vil bli lagret i input-argumentet , her listen [25,35,45]. Nå brukes listeelementene i testmetoder for sammenligningen.

Kjør nå 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ått siden zz=BB=35, og de resterende 2 testene mislykkes.

Festemetoden har et omfang bare innenfor den testfilen den er definert. Hvis vi prøver å få tilgang til fiksturen i en annen testfil, får vi en feilmelding som sier fixture 'supply_AA_BB_CC' ikke funnet for testmetodene i andre filer.

For å bruke samme fixtur mot flere testfiler, vil vi lage fixturmetoder i en fil kalt conftest.py.

La oss se dette ved PyTest-eksemplet nedenfor. Opprett 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 lete etter fixturen i testfilen først, og hvis den ikke finnes vil den lete i conftest.py

Kjør testen med py.test -k test_comparewith -v for å 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-parameterisert test

Hensikten med å parameterisere en test er å kjøre en test mot flere sett med argumenter. Vi kan gjøre dette ved å @pytest.mark.parametrize.

Vi vil se dette med PyTest-eksemplet nedenfor. Her skal vi sende 3 argumenter til en testmetode. Denne testmetoden vil legge til de to første argumentene og sammenligne dem med det tredje argumentet.

Opprett testfilen test_addition.py med koden nedenfor

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 aksepterer testmetoden 3 argumenter - input1, input2, output. Den legger til input1 og input2 og sammenligner med utgangen.

La oss kjø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 kjørt 2 ganger – en sjekker 5+5 ==10 og den andre sjekker 3+5 ==12

test_addition.py::test_add[5-5-10] BEVATT

test_addition.py::test_add[3-5-12] MISLYKKES

Pytest Xfail / Hopp over tester

Det vil være noen situasjoner der vi ikke ønsker å utføre en test, eller en testforsøk er ikke relevant for et bestemt tidspunkt. I disse situasjonene har vi muligheten til å Xfail testen eller hoppe over testene

Den xmislykkede testen vil bli utført, men den vil ikke bli regnet som delvis mislykkede eller beståtte tester. Det vil ikke vises noen sporing hvis testen mislykkes. Vi kan mislykkes tester ved hjelp av

@pytest.mark.xfail.

Å hoppe over en test betyr at testen ikke vil bli utført. Vi kan hoppe over tester ved hjelp av

@pytest.mark.hopp.

Rediger test_addition.py med koden nedenfor

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 hoppes over og vil ikke bli utført.
  • test_add_3 og test_add_4 mislyktes. Disse testene vil bli utført og vil være en del av xmislykkede (ved testfeil) eller xbestått (ved testbestått) tester. Det vil ikke være noen sporing for feil.
  • test_add_5 og test_add_6 vil bli utført og test_add_6 vil rapportere feil med tilbakesporing mens test_add_5 består

Utfør testen med 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 lage testresultater i XML-format som vi kan mate til Continuous Integration-servere for videre behandling og så. Dette kan gjøres ved

py.test test_sample1.py -v –junitxml="result.xml"

Resultatet.xml vil registrere testkjøringsresultatet. Finn et eksempelresultat.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 totalt to tester hvorav en er ikke bestått. Nedenfor kan du se detaljene om hver utførte test under tag.

Pytest Framework Tester en API

Nå skal vi lage et lite pytest-rammeverk for å teste en API. API-en som brukes her er en gratis fra https://reqres.in/. Denne nettsiden er kun for å gi testbar API. Denne nettsiden lagrer ikke våre data.

Her skal vi skrive noen tester for

  • liste noen brukere
  • logge inn med brukere

Lag filene nedenfor med koden som er gitt

conftest.py – ha en armatur som vil levere base-url for alle testmetodene

import pytest
@pytest.fixture
def supply_url():
	return "https://reqres.in/api"

test_list_user.py – inneholder testmetodene for å liste opp gyldige og ugyldige brukere

  • test_list_valid_user tester for gyldig brukerhenting og bekrefter svaret
  • test_list_invaliduser tester for ugyldig brukerhenting og bekrefter 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 – inneholder testmetoder for å teste påloggingsfunksjonalitet.

  • test_login_valid tester det gyldige påloggingsforsøket med e-post og passord
  • test_login_no_password tester det ugyldige påloggingsforsøket uten å sende passord
  • test_login_no_email tester det ugyldige påloggingsforsøket uten å sende 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

Kjør testen 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

Oppdater testene og prøv ulike utganger

Sammendrag

I denne PyTest-opplæringen dekket vi

  • Installer pytest ved hjelp av pip installasjon pytest=2.9.1
  • Enkelt pytest-program og kjør det med kommandoen py.test.
  • Påstandsutsagn, assert x==y, vil returnere enten True eller False.
  • Hvordan pytest identifiserer testfiler og metoder.
  • Test filer som starter med test_ eller slutter med _test
  • Testmetoder som starter med test
  • py.test-kommandoen vil kjøre alle testfilene i den mappen og undermappene. For å kjøre en bestemt fil kan vi bruke kommandoen py.test
  • Kjør et undersett av testmetoder
  • Gruppering av testnavn etter understreng matching.py.test -k -v vil kjøre alle testene som har i navnet.
  • Kjør test etter markører. Merk testene med @pytest.mark. og kjør testene med pytest -m å kjøre tester merket som .
  • Kjør tester parallelt
  • Installer pytest-xdist ved å bruke pip install pytest-xdist
  • Kjør tester med py.test -n NUM der NUM er antall arbeidere
  • Opprette fixturmetoder for å kjøre kode før hver test ved å merke metoden med @pytest.fixture
  • Omfanget av en fixturmetode er innenfor filen den er definert.
  • En fixturmetode kan nås på tvers av flere testfiler ved å definere den i filen conftest.py.
  • En testmetode kan få tilgang til en Pytest-fixtur ved å bruke den som et input-argument.
  • Parametriserte tester for å kjøre den mot flere sett med innganger.
    @pytest.mark.parametrize(“input1, input2, output”,[(5,5,10),(3,5,12)])
    def test_add(input1, input2, output):
    hevde inngang1+inngang2 == utgang,”mislyktes”
    vil kjøre testen med innganger (5,5,10) og (3,5,12)
  • Hopp over/xfail tester med @pytets.mark.skip og @pytest.mark.xfail
  • Lag testresultater i XML-format som dekker utførte testdetaljer ved å bruke py.test test_sample1.py -v –junitxml="result.xml"
  • Et eksempel på pytest-rammeverk for å teste en API