Урок за PyTest: какво е, как да инсталирате, рамка, твърдения

Какво е PyTest?

PyTest е тестова рамка, която позволява на потребителите да пишат тестови кодове, използвайки Python език за програмиране. Помага ви да пишете прости и мащабируеми тестови случаи за бази данни, API или потребителски интерфейс. PyTest се използва главно за писане на тестове за API. Помага за писане на тестове от прости модулни тестове до сложни функционални тестове.

Защо да използвате PyTest?

Някои от предимствата на pytest са

  • Много лесен за започване поради простия и лесен синтаксис.
  • Може да изпълнява тестове паралелно.
  • Може да изпълнява конкретен тест или подгрупа от тестове
  • Автоматично откриване на тестове
  • Пропускане на тестове
  • Отворен код

Как да инсталирате PyTest

Следва процес за инсталиране на PyTest:

Стъпка 1) Можете да инсталирате pytest от

pip install pytest==2.9.1

След като инсталацията приключи, можете да я потвърдите с чрез

py.test -h

Това ще покаже помощта

инсталирайте PyTest

Първият основен PyTest

Сега ще научим как да използваме Pytest с основен пример на PyTest.

Създайте папка study_pytest. Ще създадем нашите тестови файлове в тази папка.

Моля, отидете до тази папка в командния ред.

Създайте файл с име test_sample1.py в папката

Първият основен PyTest

Добавете кода по-долу в него и запазете

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" 

Изпълнете теста с помощта на командата

py.test

Ще получите изход като

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

Първият основен PyTest

Тук в test_sample1.py F.

F казва провал

Точка (.) означава успех.

В раздела за неуспехи можете да видите неуспешния метод(и) и линията на неуспех. Тук x==y означава 5==6, което е невярно.

След това в този урок за PyTest ще научим за твърденията в PyTest.

Твърдения в PyTest

Утвържденията на Pytest са проверки, които връщат статус True или False. в Python Pytest, ако дадено твърдение се провали в тестов метод, тогава изпълнението на този метод се спира там. Останалият код в този тестов метод не се изпълнява и твърденията на Pytest ще продължат със следващия тестов метод.

Примери за Pytest Assert:

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.

Помислете

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

Поставете този код в test_file1_method1() вместо в твърдението

assert x == y,"test failed"

Изпълнението на теста ще даде грешка като AssertionError: тестът е неуспешен x=5 y=6

Как PyTest идентифицира тестовите файлове и тестовите методи

По подразбиране pytest идентифицира само имената на файловете, започващи с тест_ или завършва с _тест като тестовите файлове. Въпреки това можем изрично да споменем други имена на файлове (обяснено по-късно). Pytest изисква имената на тестовите методи, с които да започне „тест.” Всички други имена на методи ще бъдат игнорирани дори ако изрично поискаме да изпълним тези методи.

Вижте някои примери за валидни и невалидни имена на файлове на pytest

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

Забележка: Да, можем изрично да поискаме от pytest да избере testlogin.py и logintest.py

Вижте някои примери за валидни и невалидни тестови методи на pytest

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

Забележка: Дори ако изрично споменем file1_method1() pytest няма да изпълни този метод.

Изпълнете множество тестове от конкретен файл и множество файлове

В момента в папката study_pytest имаме файл test_sample1.py. Да предположим, че имаме няколко файла, да речем test_sample2.py, test_sample3.py. За да изпълним всички тестове от всички файлове в папката и подпапките, трябва просто да изпълним командата pytest.

py.test

Това ще изпълни всички имена на файлове, започващи с test_ и имената на файлове, завършващи с _test в тази папка и подпапките в тази папка.

За да изпълняваме тестове само от определен файл, можем да използваме py.test

py.test test_sample1.py

Изпълнете подмножество от целия тест с PyTest

Понякога не искаме да изпълняваме целия тестов пакет. Pytest ни позволява да изпълняваме специфични тестове. Можем да го направим по 2 начина

  • Групиране на имена на тестове чрез съвпадение на подниз
  • Групиране на тестове по маркери

Вече имаме test_sample1.py. Създайте файл test_sample2.py и добавете кода по-долу в него

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"

Така че имаме в момента

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

Опция 1) Изпълнете тестове чрез съпоставяне на подниз

Тук трябва да изпълним всички тестове, които имат method1 в името си

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

Така че стартирането на py.test -k method1 -v ще ви даде следния резултат

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

Тук можете да видите към края 2 теста, премахнати от '-kmethod1' които са test_file1_method2 и test_file2_method2

Опитайте да бягате с различни комбинации като:-

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'

Вариант 2) Изпълнение на тестове по маркери

Pytest ни позволява да задаваме различни атрибути за тестовите методи, използвайки маркери на pytest, @pytest.mark. За да използваме маркери в тестовия файл, трябва да импортираме pytest в тестовите файлове.

Тук ще приложим различни имена на маркери към тестови методи и ще проведем конкретни тестове въз основа на имена на маркери. Можем да дефинираме маркерите на имената на всеки тест, като използваме

@pytest.mark.<name>.			

Ние дефинираме маркери set1 и set2 на тестовите методи и ще изпълним теста, използвайки имената на маркерите. Актуализирайте тестовите файлове със следния код

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"

Можем да проведем маркирания тест от

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

Изпълнете py.test -m set1. Това ще изпълни методите test_file1_method1, test_file2_method1, test_file2_method2.

Изпълнението на py.test -m set2 ще изпълни test_file1_method2.

Изпълнявайте тестове паралелно с Pytest

Обикновено тестовият пакет ще има множество тестови файлове и стотици тестови методи, чието изпълнение ще отнеме значително време. Pytest ни позволява да изпълняваме тестове паралелно.

За това трябва първо да инсталираме pytest-xdist, като стартираме

pip install pytest-xdist

Изпълнявайте тестове паралелно с Pytest

Можете да провеждате тестове сега от

py.test -n 4

-н изпълнява тестовете с помощта на множество работници. В горната команда ще има 4 работници, които да изпълнят теста.

Pytest тела

Фикстурите се използват, когато искаме да изпълним някакъв код преди всеки тестов метод. Така че вместо да повтаряме един и същ код във всеки тест, ние дефинираме приспособления. Обикновено приспособленията се използват за инициализиране на връзки към база данни, предаване на базата и т.н

Метод се маркира като приспособление на Pytest чрез маркиране с

@pytest.fixture

Тестовият метод може да използва приспособление на Pytest, като споменава приспособлението като входен параметър.

Създайте нов файл test_basic_fixture.py със следния код

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"

Тук

  • Имаме приспособление с име supply_AA_BB_CC. Този метод ще върне списък от 3 стойности.
  • Имаме 3 тестови метода, сравняващи всяка от стойностите.

Всяка от тестовите функции има входен аргумент, чието име съвпада с наличен фикстур. След това Pytest извиква съответния метод на фиксиране и върнатите стойности ще бъдат съхранени във входния аргумент, тук списъкът [25,35,45]. Сега елементите от списъка се използват в тестови методи за сравнение.

Сега изпълнете теста и вижте резултата

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

Тестът test_comparewithBB е преминат, тъй като zz=BB=35, а останалите 2 теста са неуспешни.

Методът на фиксиране има обхват само в този тестов файл, в който е дефиниран. Ако се опитаме да получим достъп до приспособлението в някой друг тестов файл, ще получим грешка, казваща приспособление 'supply_AA_BB_CC' не е намерен за тестовите методи в други файлове.

За да използваме една и съща система за фиксиране срещу множество тестови файлове, ще създадем методи за фиксиране във файл, наречен conftest.py.

Нека видим това чрез примера на PyTest по-долу. Създайте 3 файла conftest.py, test_basic_fixture.py, test_basic_fixture2.py със следния код

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 първо ще търси фикстурата в тестовия файл и ако не бъде намерен, ще търси в conftest.py

Изпълнете теста чрез py.test -k test_comparewith -v, за да получите резултата, както е показано по-долу

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 параметризиран тест

Целта на параметризирането на тест е да се изпълни тест срещу множество набори от аргументи. Можем да направим това чрез @pytest.mark.parametrize.

Ще видим това с примера на PyTest по-долу. Тук ще предадем 3 аргумента на тестов метод. Този тестов метод ще добави първите 2 аргумента и ще ги сравни с 3-тия аргумент.

Създайте тестовия файл test_addition.py с кода по-долу

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"

Тук тестовият метод приема 3 аргумента - input1, input2, output. Той добавя input1 и input2 и сравнява с изхода.

Нека изпълним теста чрез py.test -k test_add -v и да видим резултата

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

Можете да видите, че тестовете се изпълняват 2 пъти – единият проверява 5+5 ==10, а другият проверява 3+5 ==12

test_addition.py::test_add[5-5-10] УДАРЕНО

test_addition.py::test_add[3-5-12] НЕУСПЕШНО

Pytest Xfail / Пропускане на тестове

Ще има някои ситуации, в които не искаме да изпълним тест, или a тестов случай не е от значение за определено време. В тези ситуации имаме опцията да преминем Xfail на теста или да пропуснем тестовете

Тестът xfailed ще бъде изпълнен, но няма да се отчете като част от неуспешни или преминали тестове. Няма да се показва обратно проследяване, ако този тест е неуспешен. Можем да използваме xfail тестове

@pytest.mark.xfail.

Пропускането на тест означава, че тестът няма да бъде изпълнен. Можем да пропуснем тестовете с помощта на

@pytest.mark.skip.

Редактирайте test_addition.py с кода по-долу

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"

Тук

  • test_add_1 и test_add_2 се пропускат и няма да бъдат изпълнени.
  • test_add_3 и test_add_4 са xfailed. Тези тестове ще бъдат изпълнени и ще бъдат част от xfailed (при неуспешен тест) или xpassed (при преминаване на теста) тестове. Няма да има обратно проследяване за грешки.
  • test_add_5 и test_add_6 ще бъдат изпълнени и test_add_6 ще докладва за грешка с проследяване, докато test_add_5 преминава

Изпълнете теста чрез py.test test_addition.py -v и вижте резултата

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

Резултати XML

Можем да създадем резултати от тестове в XML формат, които можем да подадем към сървърите за непрекъсната интеграция за по-нататъшна обработка и т.н. Това може да стане чрез

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

Result.xml ще запише резултата от изпълнението на теста. Намерете примерен result.xml по-долу

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

от можем да видим общо два теста, единият от които е неуспешен. По-долу можете да видите подробности относно всеки изпълнен тест под етикет.

Pytest Framework Тестване на API

Сега ще създадем малка рамка на pytest за тестване на API. Използваният тук API е безплатен от https://reqres.in/. Този уебсайт е само за предоставяне на API за тестване. Този уебсайт не съхранява нашите данни.

Тук ще напишем няколко теста за

  • изброяване на някои потребители
  • влизане с потребители

Създайте файловете по-долу с дадения код

conftest.py – има фиксиране, което ще предостави основен URL адрес за всички тестови методи

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

test_list_user.py – съдържа тестовите методи за изброяване на валидни и невалидни потребители

  • test_list_valid_user тества за валидно извличане на потребител и проверява отговора
  • test_list_invaliduser тества за невалидно извличане на потребители и проверява отговора
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 – съдържа тестови методи за тестване на функционалността за влизане.

  • test_login_valid тества валидния опит за влизане с имейл и парола
  • test_login_no_password тества невалидния опит за влизане без подаване на парола
  • test_login_no_email тества невалидния опит за влизане без предаване на имейл.
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

Изпълнете теста с помощта на py.test -v

Вижте резултата като

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

Актуализирайте тестовете и опитайте различни резултати

Oбобщение

В този урок на PyTest разгледахме

  • Инсталирайте pytest с помощта на pip инсталиране pytest=2.9.1
  • Проста програма pytest и я стартирайте с команда py.test.
  • Изявленията за твърдения, assert x==y, ще върнат True или False.
  • Как pytest идентифицира тестови файлове и методи.
  • Тестови файлове, започващи с тест_ или завършва с _тест
  • Методи за изпитване, започващи с тест
  • py.test команда ще стартира всички тестови файлове в тази папка и подпапки. За да стартираме конкретен файл, можем да използваме командата py.test
  • Изпълнете подмножество от тестови методи
  • Групиране на имена на тестове по подниз matching.py.test -k -v ще изпълни всички тестове, имащи в нейното име.
  • Изпълнете тест по маркери. Маркирайте тестовете с помощта на @pytest.mark. и изпълнете тестовете с помощта на pytest -m за провеждане на тестове, маркирани като .
  • Изпълнявайте тестове паралелно
  • Инсталирайте pytest-xdist с помощта на pip install pytest-xdist
  • Изпълнете тестове, като използвате py.test -n NUM, където NUM е броят на работниците
  • Създаване на методи за фиксиране за изпълнение на код преди всеки тест чрез маркиране на метода с @pytest.fixture
  • Обхватът на метода на фиксиране е в рамките на файла, който е дефиниран.
  • Методът на фиксиране може да бъде достъпен в множество тестови файлове, като го дефинирате във файла conftest.py.
  • Тестов метод може да има достъп до Pytest fixture, като го използва като входен аргумент.
  • Тестове за параметризиране, за да го изпълните срещу множество набори от входове.
    @pytest.mark.parametrize(“вход1, вход2, изход”,[(5,5,10),(3,5,12)])
    def test_add(вход1, вход2, изход):
    assert input1+input2 == изход,”неуспешно”
    ще изпълни теста с входове (5,5,10) и (3,5,12)
  • Skip/xfail тестове с помощта на @pytets.mark.skip и @pytest.mark.xfail
  • Създайте резултати от тестове в XML формат, който обхваща подробности за изпълнените тестове, като използвате py.test test_sample1.py -v –junitxml=”result.xml”
  • Примерна рамка на pytest за тестване на API