Учебное пособие по PyTest:что такое, как установить, фреймворк, утверждения
Что такое PyTest?
PyTest — это среда тестирования, которая позволяет пользователям писать тестовые коды с использованием языка программирования Python. Это поможет вам написать простые и масштабируемые тестовые примеры для баз данных, API или пользовательского интерфейса. PyTest в основном используется для написания тестов для API. Это помогает писать тесты от простых модульных тестов до сложных функциональных тестов.
Зачем использовать PyTest?
Некоторые из преимуществ pytest
- С ним очень легко начать работу благодаря простому и понятному синтаксису.
- Может запускать тесты параллельно.
- Может запускать определенный тест или подмножество тестов.
- Автоматическое обнаружение тестов
- Пропустить тесты
- Открытый код
Из этого руководства по Python PyTest вы узнаете:
- Что такое PyTest?
- Зачем использовать PyTest?
- Как установить PyTest
- Первый базовый PyTest
- Утверждения в PyTest
- Как PyTest идентифицирует тестовые файлы и методы тестирования
- Запуск нескольких тестов из определенного файла и нескольких файлов
- Выполнить подмножество всего теста с помощью PyTest
- Выполняйте тесты параллельно с Pytest
- Фиксаторы Pytest
- Параметрический тест Pytest
- Pytest Xfail / Пропустить тесты
- XML результатов
- Pytest Framework Тестирование API
Как установить PyTest
Ниже описан процесс установки PyTest:
Шаг 1) Вы можете установить pytest с помощью
pip install pytest==2.9.1
После завершения установки вы можете подтвердить ее с помощью
py.test -h
Это отобразит справку
Первый базовый PyTest
Теперь мы узнаем, как использовать Pytest на базовом примере PyTest.
Создайте папку Study_pytest. Мы собираемся создать наши тестовые файлы в этой папке.
Перейдите к этой папке в командной строке.
Создайте файл с именем test_sample1.py внутри папки
Добавьте в него приведенный ниже код и сохраните
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
Здесь, в 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:test failed x=5 y=6
Как PyTest идентифицирует тестовые файлы и методы тестирования
По умолчанию pytest идентифицирует только имена файлов, начинающиеся с test_. или заканчивающийся на _test как тестовые файлы. Однако мы можем явно указать другие имена файлов (объясняется позже). Pytest требует, чтобы имена тестовых методов начинались с "test ». Все остальные имена методов будут игнорироваться, даже если мы явно попросим запустить эти методы.
Посмотрите несколько примеров допустимых и недопустимых имен файлов 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 позволяет нам запускать определенные тесты. Мы можем сделать это двумя способами
- Группировка имен тестов по совпадению подстрок
- Группировка тестов по маркерам
У нас уже есть 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
Теперь вы можете запускать тесты,
py.test -n 4
-n <число> запускает тесты с использованием нескольких рабочих процессов. В приведенной выше команде будет 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 аргумента тестовому методу. Этот тестовый метод добавит первые два аргумента и сравнит их с третьим аргументом.
Создайте тестовый файл 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 / Пропустить тесты
Будут некоторые ситуации, когда мы не хотим выполнять тест или тест-кейс не актуален в определенное время. В таких ситуациях у нас есть возможность Xпровалить тест или пропустить тесты
Тест 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 будет записан результат выполнения теста. Найдите образец результата.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':'[email protected]','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':'[email protected]'} 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
Обновите тесты и попробуйте разные результаты
Обзор
В этом руководстве по PyTest мы рассмотрели
- Установите pytest с помощью pip install pytest=2.9.1
- Простая программа pytest и запуск с помощью команды py.test.
- Инструкции утверждения, assert x==y, вернут либо True, либо False.
- Как pytest идентифицирует тестовые файлы и методы.
- Тестовые файлы, начинающиеся с test_ или заканчивающийся на _test
- Методы тестирования, начинающиеся с test
- Команда py.test запустит все тестовые файлы в этой папке и подпапках. Чтобы запустить определенный файл, мы можем использовать команду py.test
- Выполнить подмножество тестовых методов
- Группировка имен тестов по подстроке соответствия.py.test -k
-v запустит все тесты, в названии которых есть . - Выполнить тест по маркерам. Отметьте тесты с помощью @pytest.mark.
и запустите тесты с помощью pytest -m , чтобы запустить тесты, помеченные как . - Выполнять тесты параллельно
- Установите pytest-xdist с помощью pip install pytest-xdist
- Выполнить тесты, используя py.test -n NUM, где NUM – количество рабочих процессов
- Создание методов фиксации для запуска кода перед каждым тестом путем пометки метода @pytest.fixture
- Область действия метода фиксации находится в пределах файла, в котором он определен.
- Доступ к методу фикстуры можно получить в нескольких тестовых файлах, определив его в файле conftest.py.
- Метод теста может получить доступ к фикстуре Pytest, используя ее в качестве входного аргумента.
- Параметризация тестов для запуска с несколькими наборами входных данных.
@pytest.mark.parametrize("input1, input2, output", [(5,5,10),(3,5,12)] )
def test_add(input1, input2, output):
assert input1+input2 ==output”failed”
запустит тест с входными данными (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
Python
- Как установить WordPress в Google Cloud
- Учебное пособие по платформе автоматизации тестирования закодированного пользовательского интерфейса
- Что такое ключ безопасности сети? Как его найти?
- Что такое звонки по WiFi? Как это работает?
- Что такое 6G и насколько быстро он будет?
- Как установить выравниватель док-станции
- Тестирование частичного разряда:что это такое и как оно работает
- Что такое кавитация насоса и как ее избежать?
- Что такое медная пайка и как это сделать?
- Что такое невыполненная работа по обслуживанию? Как это преодолеть?