Tutorial PyTest: Ce este PyTest? (cu exemple)

โšก Rezumat inteligent

Pytest este un Python Cadru de testare care vฤƒ ajutฤƒ sฤƒ scrieศ›i teste simple ศ™i scalabile pentru baze de date, API-uri ศ™i interfeศ›e utilizator. Acesta acceptฤƒ fixฤƒri, parametrizare, markeri, rulฤƒri paralele ศ™i raportare detaliatฤƒ a aserศ›iunilor, de la teste unitare de bazฤƒ la teste funcศ›ionale complexe.

  • ๐Ÿ”˜ Instalare: Comanda pip install pytest adaugฤƒ framework-ul, iar py.test -h confirmฤƒ configurarea prin afiศ™area rezultatului de ajutor.
  • โ˜‘๏ธ Descoperirea testului: Pytest colecteazฤƒ automat fiศ™ierele numite test_*.py sau *_test.py ศ™i metodele ale cฤƒror nume รฎncep cu test.
  • โœ… Afirmaศ›ii: Instrucศ›iunile de afirmaศ›ie simple raporteazฤƒ o AssertionError clarฤƒ cu valorile comparate ori de cรขte ori o verificare eศ™ueazฤƒ.
  • ๐Ÿงช Program: Decoratorul @pytest.fixture furnizeazฤƒ date de configurare reutilizabile care sunt partajate รฎntre fiศ™iere prin intermediul unui fiศ™ier conftest.py.
  • ๐Ÿ› ๏ธ Curse selective: Markerii, filtrul de subศ™ir -k ศ™i parametrizarea executฤƒ subseturi vizate sau acelaศ™i test pe mai multe intrฤƒri.
  • ๐Ÿค– Fluxuri de lucru bazate pe inteligenศ›a artificialฤƒ: Echipele de รฎnvฤƒศ›are automatฤƒ folosesc pytest pentru a valida canalele de date ศ™i rezultatele modelelor, iar Copilot poate redacta cazuri de testare.

Tutorial Pytest

Pytest este unul dintre cele mai utilizate framework-uri de testare din Python ecosistem. Menศ›ine codul de testare scurt ศ™i uศ™or de citit, descoperฤƒ automat testele ศ™i se dezvoltฤƒ de la o primฤƒ aserศ›iune รฎntr-o suitฤƒ completฤƒ de teste pentru baze de date, API-uri ศ™i interfeศ›e utilizator. Secศ›iunile de mai jos prezintฤƒ instalarea, aserศ›iunile, fixฤƒrile, markerii, parametrizarea ศ™i rulฤƒrile paralele cu exemple practice.

Ce este Pytest?

Pytest este un framework de testare care vฤƒ permite sฤƒ scrieศ›i cod de testare folosind Python limbaj de programare. Vฤƒ ajutฤƒ sฤƒ scrieศ›i cazuri de testare simple ศ™i scalabile pentru baze de date, API-uri ศ™i chiar interfeศ›e utilizator. Pytest este utilizat รฎn principal pentru scrierea de teste pentru API-uri ศ™i se scaleazฤƒ de la teste unitare simple la teste funcศ›ionale complexe.

De ce sฤƒ folosiศ›i Pytest?

Cรขteva dintre avantajele pytest sunt:

  • Foarte uศ™or de utilizat pentru รฎnceput, datoritฤƒ sintaxei sale simple ศ™i clare.
  • Poate rula teste รฎn paralel.
  • Poate rula un test specific sau un subset de teste.
  • Detecteazฤƒ automat testele.
  • Se pot sฤƒri peste teste.
  • Sursa deschisa.

Cum se instaleazฤƒ Pytest

Urmeazฤƒ procesul de instalare a pytest-ului:

Pas 1) Puteศ›i instala pytest cu comanda de mai jos.

pip install pytest==2.9.1

Dupฤƒ ce instalarea este completฤƒ, o puteศ›i confirma cu urmฤƒtoarea comandฤƒ.

py.test -h

Aceasta va afiศ™a ajutorul.

Instalaศ›i pytest ศ™i vizualizaศ›i rezultatul ajutorului

Primul exemplu de bazฤƒ Pytest

Acum, veศ›i รฎnvฤƒศ›a cum sฤƒ utilizaศ›i pytest cu un exemplu pytest de bazฤƒ.

Creaศ›i un folder study_pytest. Veศ›i crea fiศ™ierele de test รฎn interiorul acestui folder. Navigaศ›i la acel folder รฎn linia de comandฤƒ. Creaศ›i un fiศ™ier numit test_sample1.py รฎn interiorul folderului.

Creaศ›i primul fiศ™ier de test pytest de bazฤƒ

Adฤƒugaศ›i codul de mai jos รฎn el ศ™i salvaศ›i.

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" 

Rulaศ›i testul folosind comanda de mai jos.

py.test

Veศ›i obศ›ine rezultatul dupฤƒ cum urmeazฤƒ.

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

Rezultatul eศ™ecului testului Pytest

Aici, รฎn test_sample1.py F., litera F indicฤƒ eศ™ec, iar punctul (.) indicฤƒ succes. รŽn secศ›iunea de eศ™ecuri, puteศ›i vedea metoda (metodele) eศ™uatฤƒ(e) ศ™i linia de eศ™ec. Aici x==y รฎnseamnฤƒ 5==6, ceea ce este fals.

รŽn continuare, veศ›i รฎnvฤƒศ›a despre aserศ›iuni รฎn pytest.

Afirmaศ›ii รฎn Pytest

Aserศ›iunile Pytest sunt verificฤƒri care returneazฤƒ fie starea Adevฤƒrat, fie Fals. รŽn pytest, dacฤƒ o aserศ›iune eศ™ueazฤƒ รฎntr-o metodฤƒ de testare, atunci execuศ›ia acelei metode este opritฤƒ acolo. Codul rฤƒmas din acea metodฤƒ de testare nu este executat, iar pytest continuฤƒ cu urmฤƒtoarea metodฤƒ de testare.

Exemple de aserศ›iuni Pytest:

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.

Luaศ›i รฎn considerare urmฤƒtoarele.

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

Plasaศ›i acest cod รฎn test_file1_method1() รฎn loc de aserศ›iunea de mai jos.

assert x == y,"test failed"

Rularea testului va da eศ™ecul ca AssertionError: testul eศ™uat x=5 y=6.

Cum identificฤƒ Pytest fiศ™ierele de testare ศ™i metodele de testare

รŽn mod implicit, pytest identificฤƒ doar numele fiศ™ierelor care รฎncep cu Test_ sau se terminฤƒ cu _Test ca fiศ™iere de test. Putem menศ›iona explicit ศ™i alte nume de fiศ™iere (explicaศ›ii mai tรขrziu). Pytest necesitฤƒ ca numele metodelor de testare sฤƒ รฎnceapฤƒ cu testToate celelalte nume de metode sunt ignorate, chiar dacฤƒ solicitฤƒm explicit rularea acelor metode.

Vedeศ›i cรขteva exemple de nume de fiศ™iere pytest valide ศ™i nevalide.

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

Notฤƒ: Da, putem solicita รฎn mod explicit lui pytest sฤƒ selecteze testlogin.py ศ™i logintest.py.

Vedeศ›i cรขteva exemple de metode de testare pytest valide ศ™i nevalide.

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

Notฤƒ: Chiar dacฤƒ menศ›ionฤƒm explicit file1_method1(), pytest nu va rula aceastฤƒ metodฤƒ.

Rulaศ›i mai multe teste dintr-un anumit fiศ™ier ศ™i mai multe fiศ™iere

รŽn prezent, รฎn folderul study_pytest, avem fiศ™ierul test_sample1.py. Sฤƒ presupunem cฤƒ avem mai multe fiศ™iere, de exemplu test_sample2.py ศ™i test_sample3.py. Pentru a rula toate testele din toate fiศ™ierele din folder ศ™i subfoldere, trebuie doar sฤƒ rulฤƒm comanda pytest.

py.test

Aceasta va rula toate numele de fiศ™iere care รฎncep cu test_ ศ™i numele de fiศ™iere care se terminฤƒ cu _test din acel folder ศ™i din subfolderele din acel folder.

Pentru a rula teste doar dintr-un anumit fiศ™ier, putem folosi py.test .

py.test test_sample1.py

Executaศ›i un subset al รฎntregului test cu Pytest

Uneori nu dorim sฤƒ rulฤƒm รฎntreaga suitฤƒ de teste. Pytest ne permite sฤƒ rulฤƒm teste specifice. Putem face acest lucru รฎn douฤƒ moduri:

  • Grouping a numelor de teste prin potrivirea subศ™irurilor.
  • Grouping a testelor prin corectori.

Avem deja fiศ™ierul test_sample1.py. Creaศ›i un fiศ™ier test_sample2.py ศ™i adฤƒugaศ›i codul de mai jos รฎn el.

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"

Deci avem urmฤƒtoarele รฎn prezent.

โ€ข test_sample1.py
โ€ข test_file1_method1()
โ€ข test_file1_method2()
โ€ข test_sample2.py
โ€ข test_file2_method1()
โ€ข test_file2_method2()

Opศ›iunea 1) Executaศ›i teste prin potrivirea subศ™irurilor

Aici, pentru a rula toate testele care au metoda1 รฎn nume, trebuie sฤƒ rulฤƒm comanda de mai jos.

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

Deci, rularea comenzii py.test -k method1 -v vฤƒ va oferi urmฤƒtorul rezultat.

test_sample2.py::test_file2_method1 FAILED
test_sample1.py::test_file1_method1 FAILED

============================================== FAILURES ==============================================
_________________________________________ test_file2_method1 _________________________________________
    def test_file2_method1():
    	x=5
    	y=6
       	assert x+1 == y,"test failed"
>      	assert x == y,"test failed because x=" + str(x) + " y=" + str(y)
E       AssertionError: test failed because x=5 y=6
E       assert 5 == 6
test_sample2.py:5: AssertionError

_________________________________________ test_file1_method1 _________________________________________
    @pytest.mark.only
    def test_file1_method1():
    	x=5
    	y=6
       	assert x+1 == y,"test failed"
>      	assert x == y,"test failed because x=" + str(x) + " y=" + str(y)
E       AssertionError: test failed because x=5 y=6
E       assert 5 == 6
test_sample1.py:8: AssertionError

================================= 2 tests deselected by '-kmethod1' ==================================
=============================== 2 failed, 2 deselected in 0.02 seconds ===============================

Aici puteศ›i vedea, spre sfรขrศ™it, 2 teste deselectate de โ€ž-kmethod1โ€, care sunt test_file1_method2 ศ™i test_file2_method2.

รŽncearcฤƒ sฤƒ rulezi cu diverse combinaศ›ii, cum ar fi urmฤƒtoarele.

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'

Opศ›iunea 2) Rulaศ›i teste prin markeri

Pytest ne permite sฤƒ setฤƒm diverse atribute pentru metodele de testare folosind markere pytest, @pytest.mark. Pentru a utiliza markere รฎn fiศ™ierul de test, trebuie sฤƒ importฤƒm pytest รฎn fiศ™ierele de test. Aici vom aplica diferite nume de markere metodelor de testare ศ™i vom rula teste specifice pe baza numelor markerelor. Putem defini markerele pentru fiecare nume de test folosind urmฤƒtoarele.

@pytest.mark.<name>.			

Definim markerii set1 ศ™i set2 pe metodele de testare ศ™i vom rula testul folosind numele markerilor. Actualizaศ›i fiศ™ierele de test cu urmฤƒtorul cod.

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"

Putem rula testul marcat cu comanda de mai jos.

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

Ruleazฤƒ comanda py.test -m set1. Aceasta va rula metodele test_file1_method1, test_file2_method1 ศ™i test_file2_method2. Rularea comenzii py.test -m set2 va rula comanda test_file1_method2.

Executarea testelor รฎn paralel cu Pytest

De obicei, o suitฤƒ de teste va avea mai multe fiศ™iere de testare ศ™i sute de metode de testare, a cฤƒror execuศ›ie va dura considerabil. Pytest ne permite sฤƒ rulฤƒm teste รฎn paralel. Pentru aceasta, trebuie mai รฎntรขi sฤƒ instalฤƒm pytest-xdist rulรขnd comanda de mai jos.

pip install pytest-xdist

Rulaศ›i testele รฎn paralel cu pytest-xdist

Puteศ›i rula testele acum cu urmฤƒtoarea comandฤƒ.

py.test -n 4

Aici, -n executฤƒ testele folosind mai mulศ›i workeri. รŽn comanda de mai sus, vor exista patru workeri pentru a executa testele.

Pytest Fixtures

Fixฤƒrile sunt folosite atunci cรขnd dorim sฤƒ rulฤƒm un anumit cod รฎnainte de fiecare metodฤƒ de testare. Aศ™adar, รฎn loc sฤƒ repetฤƒm โ€‹โ€‹acelaศ™i cod รฎn fiecare test, definim fixฤƒri. De obicei, fixฤƒrile sunt folosite pentru a iniศ›ializa conexiunile la baza de date, a transmite codul de bazฤƒ URL, ศ™i aศ™a mai departe. O metodฤƒ este marcatฤƒ ca fixturฤƒ pytest prin marcarea ei cu urmฤƒtoarele.

@pytest.fixture

O metodฤƒ de testare poate utiliza o fixturฤƒ pytest menศ›ionรขnd fixtura ca parametru de intrare. Creaศ›i un fiศ™ier nou test_basic_fixture.py cu urmฤƒtorul cod.

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"

Aici:

  • Avem un dispozitiv de fixare numit supply_AA_BB_CC. Aceastฤƒ metodฤƒ returneazฤƒ o listฤƒ de trei valori.
  • Avem trei metode de testare care comparฤƒ fiecare dintre valori.

Fiecare dintre funcศ›iile de test are un argument de intrare al cฤƒrui nume se potriveศ™te cu un fixture disponibil. Pytest invocฤƒ apoi metoda fixture corespunzฤƒtoare, iar valorile returnate sunt stocate รฎn argumentul de intrare, aici lista [25,35,45]. Elementele listei sunt apoi utilizate รฎn metodele de testare pentru comparaศ›ie. Acum rulaศ›i testul ศ™i vedeศ›i rezultatul.

 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 =================================

Testul test_comparewithBB reuศ™eศ™te, deoarece zz=BB=35, iar celelalte douฤƒ teste eศ™ueazฤƒ.

Metoda fixture are un domeniu de aplicare doar รฎn fiศ™ierul de test รฎn care este definitฤƒ. Dacฤƒ รฎncercฤƒm sฤƒ accesฤƒm fixture-ul รฎn alt fiศ™ier de test, vom primi o eroare care spune fixture โ€žsupply_AA_BB_CCโ€ nu a fost gฤƒsit pentru metodele de testare din celelalte fiศ™iere.

Pentru a utiliza aceeaศ™i fixturฤƒ pentru mai multe fiศ™iere de test, creฤƒm metode fixture รฎntr-un fiศ™ier numit conftest.py. Sฤƒ vedem acest lucru cu exemplul pytest de mai jos. Creaศ›i trei fiศ™iere, conftest.py, test_basic_fixture.py ศ™i test_basic_fixture2.py, cu urmฤƒtorul cod.

concurs.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 va cฤƒuta mai รฎntรขi fixtura รฎn fiศ™ierul de test, iar dacฤƒ nu o gฤƒseศ™te, o va cฤƒuta รฎn conftest.py. Rulaศ›i testul cu comanda py.test -k test_comparewith -v pentru a obศ›ine rezultatul de mai jos.

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

Testul parametrizat Pytest

Scopul parametrizฤƒrii unui test este de a rula un test pe mai multe seturi de argumente. Putem face acest lucru cu @pytest.mark.parametrize. Vom vedea acest lucru รฎn exemplul pytest de mai jos. Aici vom transmite trei argumente unei metode de testare. Aceastฤƒ metodฤƒ de testare va adฤƒuga primele douฤƒ argumente ศ™i va compara rezultatul cu al treilea argument. Creaศ›i fiศ™ierul de test test_addition.py cu codul de mai jos.

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"

Aici, metoda de testare acceptฤƒ trei argumente: input1, input2 ศ™i output. Adunฤƒ input1 ศ™i input2 ศ™i comparฤƒ suma cu output-ul. Sฤƒ rulฤƒm testul cu py.test -k test_add -v ศ™i sฤƒ vedem rezultatul.

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

Puteศ›i vedea testele rulate de douฤƒ ori, unul verificรขnd 5+5 ==10 ศ™i celฤƒlalt verificรขnd 3+5 ==12.

test_addition.py::test_add[5-5-10] A TRUS

test_addition.py::test_add[3-5-12] A ESCUT

Pytest Xfail ศ™i teste de omitere

Vor exista unele situaศ›ii รฎn care nu dorim sฤƒ executฤƒm un test sau un caz de testare nu este relevant la un moment dat. รŽn aceste situaศ›ii, avem opศ›iunea de a eศ™ua testul sau de a sฤƒri peste teste. Testul eศ™uat va fi executat, dar nu va fi considerat ca parte a testelor eศ™uate sau reuศ™ite. Nu va exista nicio traceback afiศ™at dacฤƒ testul respectiv eศ™ueazฤƒ. Putem face teste xfail folosind @pytest.mark.xfail. Sari pesteping Un test รฎnseamnฤƒ cฤƒ testul nu va fi executat. Putem sฤƒri peste teste folosind @pytest.mark.skip. Editaศ›i fiศ™ierul test_addition.py cu codul de mai jos.

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"

Aici:

  • test_add_1 ศ™i test_add_2 sunt ignorate ศ™i nu vor fi executate.
  • test_add_3 ศ™i test_add_4 sunt xfailed. Aceste teste vor fi executate ศ™i vor face parte din testele xfailed (la eศ™ecul testului) sau xpassed (la succesul testului). Nu va exista nicio traceback pentru eศ™ecuri.
  • test_add_5 ศ™i test_add_6 vor fi executate, iar test_add_6 va raporta o eroare cu un traceback รฎn timp ce test_add_5 trece.

Executaศ›i testul cu comanda py.test test_addition.py -v ศ™i vedeศ›i rezultatul.

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 =================

Rezultate รฎn format XML

Putem crea rezultate ale testelor รฎn format XML, pe care le putem transmite serverelor de integrare continuฤƒ pentru procesare ulterioarฤƒ. Acest lucru se poate face cu comanda py.test test_sample1.py -v โ€“junitxml=โ€result.xmlโ€. Fiศ™ierul result.xml va รฎnregistra rezultatul execuศ›iei testului. Gฤƒsiศ›i un exemplu de fiศ™ier result.xml mai jos.

<?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>

Din Putem vedea un total de douฤƒ teste, dintre care unul a eศ™uat. Mai jos, puteศ›i vedea detaliile referitoare la fiecare test executat รฎn cadrul etichetฤƒ.

Framework-ul Pytest pentru testarea unei API-uri

Acum vom crea un mic framework pytest pentru a testa o API. API-ul folosit aici este unul gratuit de la reqres.inAcest site web oferฤƒ pur ศ™i simplu o API testabilฤƒ ศ™i nu stocheazฤƒ datele noastre. Aici vom scrie cรขteva teste pentru urmฤƒtoarele:

  • Listรขnd cรขศ›iva utilizatori.
  • Autentificare cu utilizatori.

Creaศ›i fiศ™ierele de mai jos cu codul dat. Mai รฎntรขi, conftest.py are un dispozitiv fix care va furniza codul de bazฤƒ URL pentru toate metodele de testare.

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

Apoi, test_list_user.py conศ›ine metodele de testare pentru listarea utilizatorilor valizi ศ™i invalidi.

  • test_list_valid_user testeazฤƒ o funcศ›ie fetch validฤƒ a utilizatorului ศ™i verificฤƒ rฤƒspunsul.
  • test_list_invaliduser testeazฤƒ pentru o funcศ›ie fetch invalidฤƒ a utilizatorului ศ™i verificฤƒ rฤƒspunsul.
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

Apoi, test_login_user.py conศ›ine metodele de testare pentru testarea funcศ›ionalitฤƒศ›ii de autentificare.

  • test_login_valid testeazฤƒ รฎncercarea validฤƒ de conectare cu o adresฤƒ de e-mail ศ™i o parolฤƒ.
  • test_login_no_password testeazฤƒ รฎncercarea invalidฤƒ de conectare fฤƒrฤƒ a introduce o parolฤƒ.
  • test_login_no_email testeazฤƒ รฎncercarea invalidฤƒ de conectare fฤƒrฤƒ a transmite o adresฤƒ de 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

Rulaศ›i testul folosind comanda py.test -v ศ™i vedeศ›i rezultatul dupฤƒ cum urmeazฤƒ.

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

Actualizaศ›i testele ศ™i รฎncercaศ›i diverse rezultate.

รŽntrebฤƒri frecvente

Amรขndoi aleargฤƒ Python teste, dar pytest foloseศ™te instrucศ›iuni assert simple, necesitฤƒ mai puศ›ine funcศ›ii standard ศ™i oferฤƒ fixฤƒri, markeri ศ™i parametrizare. Unittest este livrat cu biblioteca standard ศ™i urmeazฤƒ stilul xUnit bazat pe clase. Pytest poate rula รฎn continuare cazuri de testare unittest existente.

Foloseศ™te sintaxa ID-ului nodului cu douฤƒ puncte: pytest test_sample1.py::test_file1_method1. Aceasta executฤƒ doar acea metodฤƒ. De asemenea, poศ›i viza un fiศ™ier รฎntreg (pytest test_sample1.py) sau poศ›i filtra dupฤƒ nume cu opศ›iunea -k.

Instalaศ›i pluginul pytest-cov, apoi rulaศ›i pytest โ€“cov=your_package. Acesta raporteazฤƒ procentul de linii executate de testele dvs., iar โ€“cov-report=html produce un raport rฤƒsfoibil care evidenศ›iazฤƒ liniile netestate.

Un domeniu de aplicare fixture controleazฤƒ frecvenศ›a executฤƒrii: funcศ›ie (implicit), clasฤƒ, modul, pachet sau sesiune. Setaศ›i-l cu @pytest.fixture(scope="module"). Domeniile de aplicare mai largi reutilizeazฤƒ configuraศ›ii costisitoare, cum ar fi o conexiune la baza de date, รฎn mai multe teste.

Executaศ›i comanda pytest -x pentru a opri imediat dupฤƒ primul test eศ™uat sau comanda pytest โ€“maxfail=2 pentru a opri dupฤƒ un numฤƒr ales de eศ™ecuri. Aceasta scurteazฤƒ bucla de feedback atunci cรขnd depanaศ›i o suitฤƒ de erori.

Pytest captureazฤƒ stdout-ul รฎn mod implicit. Adฤƒugaศ›i indicatorul -s (pytest -s) pentru a dezactiva captura ศ™i a afiศ™a instrucศ›iunile de imprimare รฎn timp real. Indicatorul -v adaugฤƒ o ieศ™ire detaliatฤƒ, per test, care listeazฤƒ fiecare nume de test ศ™i rezultatul sฤƒu.

Echipele de รฎnvฤƒศ›are automatฤƒ folosesc pytest pentru a valida procesele de รฎncฤƒrcare a datelor, transformฤƒrile de caracteristici ศ™i ieศ™irile modelului. Fixฤƒrile furnizeazฤƒ seturi de date eศ™antion, iar parametrizarea verificฤƒ multe intrฤƒri, astfel รฎncรขt pฤƒrศ›ile inteligente ale unui sistem sฤƒ rฤƒmรขnฤƒ fiabile pe mฤƒsurฤƒ ce codul se modificฤƒ.

Da. GitHub Copilot ศ™i asistenศ›ii AI agenศ›ici pot genera funcศ›ii pytest, fixฤƒri ศ™i cazuri parametrizate dintr-un prompt scurt sau o funcศ›ie existentฤƒ. RevVizualizaศ›i aserศ›iunile generate, deoarece IA poate rata cazuri limitฤƒ ศ™i poate evalua greศ™it valorile aศ™teptate.

Rezumaศ›i aceastฤƒ postare cu: