Учебники

Pytest — Краткое руководство

Pytest — Введение

Pytest — это основанная на Python среда тестирования, которая используется для написания и выполнения тестовых кодов. В настоящее время службы REST pytest в основном используются для тестирования API, хотя мы можем использовать pytest для написания простых и сложных тестов, то есть мы можем писать коды для тестирования API, базы данных, пользовательского интерфейса и т. Д.

Преимущества Pytest

Преимущества Pytest заключаются в следующем —

  • Pytest может выполнять несколько тестов параллельно, что сокращает время выполнения набора тестов.

  • У Pytest есть собственный способ автоматического определения тестового файла и тестовых функций, если это не указано явно.

  • Pytest позволяет нам пропустить подмножество тестов во время выполнения.

  • Pytest позволяет нам запускать подмножество всего набора тестов.

  • Pytest является бесплатным и открытым исходным кодом.

  • Благодаря простому синтаксису, pytest очень прост для запуска.

Pytest может выполнять несколько тестов параллельно, что сокращает время выполнения набора тестов.

У Pytest есть собственный способ автоматического определения тестового файла и тестовых функций, если это не указано явно.

Pytest позволяет нам пропустить подмножество тестов во время выполнения.

Pytest позволяет нам запускать подмножество всего набора тестов.

Pytest является бесплатным и открытым исходным кодом.

Благодаря простому синтаксису, pytest очень прост для запуска.

В этом уроке мы объясним основы pytest с примерами программ.

Pytest — Настройка среды

В этой главе мы узнаем, как установить pytest.

Чтобы начать установку, выполните следующую команду —

pip install pytest == 2.9.1

Мы можем установить любую версию pytest. Здесь 2.9.1 — версия, которую мы устанавливаем.

Чтобы установить последнюю версию pytest, выполните следующую команду —

pip install pytest

Подтвердите установку, используя следующую команду, чтобы отобразить раздел справки pytest.

pytest -h

Определение тестовых файлов и тестовых функций

При запуске pytest без указания имени файла будут запущены все файлы формата test _ *. Py или * _test.py в текущем каталоге и подкаталогах. Pytest автоматически определяет эти файлы как тестовые файлы. Мы можем заставить pytest запускать другие имена файлов, явно упоминая их.

Pytest требует, чтобы имена тестовых функций начинались с test . Имена функций, которые не имеют формат test * , не считаются тестовыми функциями в pytest. Мы не можем явно заставить pytest рассматривать любую функцию, не начинающуюся с test, как тестовую функцию.

Мы поймем выполнение тестов в наших последующих главах.

Pytest — начиная с базового теста

Теперь мы начнем с нашей первой программы pytest. Сначала мы создадим каталог и тем самым создадим наши тестовые файлы в каталоге.

Давайте следовать шагам, показанным ниже —

  • Создайте новый каталог с именем автоматизации и перейдите в каталог в командной строке.

  • Создайте файл с именем test_square.py и добавьте приведенный ниже код в этот файл.

Создайте новый каталог с именем автоматизации и перейдите в каталог в командной строке.

Создайте файл с именем test_square.py и добавьте приведенный ниже код в этот файл.

import math

def test_sqrt():
   num = 25
   assert math.sqrt(num) == 5

def testsquare():
   num = 7
   assert 7*7 == 40

def tesequality():
   assert 10 == 11

Запустите тест с помощью следующей команды —

pytest

Приведенная выше команда сгенерирует следующий вывод:

test_square.py .F
============================================== FAILURES 
==============================================
______________________________________________ testsquare 
_____________________________________________
   def testsquare():
   num=7
>  assert 7*7 == 40
E  assert (7 * 7) == 40
test_square.py:9: AssertionError
================================= 1 failed, 1 passed in 0.06 seconds 
=================================

Смотрите первую строку результата. Он отображает имя файла и результаты. F обозначает неудачу теста, а точка (.) Обозначает успех теста.

Ниже мы можем увидеть детали неудачных тестов. Он покажет, при каком утверждении тест не пройден. В нашем примере 7 * 7 сравнивается на равенство 49, что неверно. В итоге мы можем увидеть итоги выполнения теста, 1 не пройден и 1 пройден.

Функция tescompare не выполняется, поскольку pytest не будет рассматривать ее как тест, поскольку ее имя не соответствует формату test * .

Теперь выполните приведенную ниже команду и снова увидите результат —

pytest -v

-v увеличивает многословие.

test_square.py::test_sqrt PASSED
test_square.py::testsquare FAILED
============================================== FAILURES 
==============================================
_____________________________________________ testsquare 
_____________________________________________
   def testsquare():
   num = 7
>  assert 7*7 == 40
E  assert (7 * 7) == 40
test_square.py:9: AssertionError
================================= 1 failed, 1 passed in 0.04 seconds 
=================================

Теперь результат более понятен в отношении теста, который не прошел, и теста, который прошел.

Примечание — команда pytest выполнит все файлы формата test_ * или * _test в текущем каталоге и подкаталогах.

Pytest — выполнение файлов

В этой главе мы узнаем, как выполнить один тестовый файл и несколько тестовых файлов. У нас уже есть тестовый файл test_square.py . Создайте новый тестовый файл test_compare.py со следующим кодом —

def test_greater():
   num = 100
   assert num > 100

def test_greater_equal():
   num = 100
   assert num >= 100

def test_less():
   num = 100
   assert num < 200

Теперь, чтобы запустить все тесты из всех файлов (2 файла здесь), нам нужно выполнить следующую команду —

pytest -v

Приведенная выше команда будет запускать тесты как из test_square.py, так и test_compare.py . Выход будет сгенерирован следующим образом:

test_compare.py::test_greater FAILED
test_compare.py::test_greater_equal PASSED
test_compare.py::test_less PASSED
test_square.py::test_sqrt PASSED
test_square.py::testsquare FAILED
================================================ FAILURES 
================================================
______________________________________________ test_greater 
______________________________________________
   def test_greater():
   num = 100
>  assert num > 100
E  assert 100 > 100

test_compare.py:3: AssertionError
_______________________________________________ testsquare 
_______________________________________________
   def testsquare():
   num = 7
>  assert 7*7 == 40
E  assert (7 * 7) == 40

test_square.py:9: AssertionError
=================================== 2 failed, 3 passed in 0.07 seconds 
===================================

Чтобы выполнить тесты из определенного файла, используйте следующий синтаксис —

pytest <filename> -v

Теперь выполните следующую команду —

pytest test_compare.py -v

Приведенная выше команда выполнит тесты только из файла test_compare.py. Наш результат будет —

test_compare.py::test_greater FAILED
test_compare.py::test_greater_equal PASSED
test_compare.py::test_less PASSED
============================================== FAILURES 
==============================================
____________________________________________ test_greater 
____________________________________________
   def test_greater():
   num = 100
>  assert num > 100
E  assert 100 > 100
test_compare.py:3: AssertionError
================================= 1 failed, 2 passed in 0.04 seconds 
=================================

Выполнить подмножество тестов

В реальном сценарии у нас будет несколько тестовых файлов, и каждый файл будет иметь несколько тестов. Тесты будут охватывать различные модули и функции. Предположим, мы хотим запустить только определенный набор тестов; как мы пойдем об этом?

Pytest предоставляет два способа запуска подмножества набора тестов.

  • Выберите тесты для запуска, основываясь на совпадении подстрок имен тестов.
  • Выберите группы тестов для запуска на основе примененных маркеров.

Мы объясним эти два примера в следующих главах.

Подстрока соответствия имен тестов

Для выполнения тестов, содержащих в своем имени строку, мы можем использовать следующий синтаксис:

pytest -k <substring> -v

-k <substring> представляет подстроку для поиска в именах тестов.

Теперь выполните следующую команду —

pytest -k great -v

Это выполнит все имена тестов, в названии которых содержится слово «great» . В этом случае это test_greater () и test_greater_equal () . Смотрите результат ниже.

test_compare.py::test_greater FAILED
test_compare.py::test_greater_equal PASSED
============================================== FAILURES 
==============================================
____________________________________________ test_greater 
____________________________________________
def test_greater():
num = 100
>  assert num > 100
E  assert 100 > 100
test_compare.py:3: AssertionError
========================== 1 failed, 1 passed, 3 deselected in 0.07 seconds 
==========================

Здесь в результате мы видим, что 3 теста отменены. Это потому, что в названиях этих тестов нет слова « великий» .

Примечание . Название тестовой функции должно начинаться с «test».

Pytest — группировка тестов

В этой главе мы научимся группировать тесты с помощью маркеров.

Pytest позволяет нам использовать маркеры на тестовых функциях. Маркеры используются для установки различных функций / атрибутов для тестирования функций. Pytest предоставляет множество встроенных маркеров, таких как xfail, пропуск и параметризация. Кроме того, пользователи могут создавать свои собственные имена маркеров. Маркеры применяются к тестам с использованием приведенного ниже синтаксиса:

@pytest.mark.<markername>

Чтобы использовать маркеры, мы должны импортировать модуль pytest в тестовый файл. Мы можем определить собственные имена маркеров для тестов и запустить тесты, имеющие эти имена маркеров.

Для запуска помеченных тестов мы можем использовать следующий синтаксис —

pytest -m <markername> -v

-m <markername> представляет имя маркера тестов, которые должны быть выполнены.

Обновите наши тестовые файлы test_compare.py и test_square.py следующим кодом. Мы определяем 3 маркера — отличный, квадратный, другие .

test_compare.py

import pytest
@pytest.mark.great
def test_greater():
   num = 100
   assert num > 100

@pytest.mark.great
def test_greater_equal():
   num = 100
   assert num >= 100

@pytest.mark.others
def test_less():
   num = 100
   assert num < 200

test_square.py

import pytest
import math

@pytest.mark.square
def test_sqrt():
   num = 25
   assert math.sqrt(num) == 5

@pytest.mark.square
def testsquare():
   num = 7
   assert 7*7 == 40

@pytest.mark.others
   def test_equality():
   assert 10 == 11

Теперь, чтобы запустить тесты, помеченные как другие , выполните следующую команду —

pytest -m others -v

Смотрите результат ниже. Было проведено 2 теста, помеченных как другие .

test_compare.py::test_less PASSED
test_square.py::test_equality FAILED
============================================== FAILURES
==============================================
___________________________________________ test_equality
____________________________________________
   @pytest.mark.others
   def test_equality():
>  assert 10 == 11
E  assert 10 == 11
test_square.py:16: AssertionError
========================== 1 failed, 1 passed, 4 deselected in 0.08 seconds
==========================

Точно так же мы можем запускать тесты и с другими маркерами — отлично, сравните

Pytest — Светильники

Светильники — это функции, которые запускаются перед каждой тестовой функцией, к которой она применяется. Светильники используются для подачи некоторых данных в тесты, таких как соединения с базой данных, URL-адреса для тестирования и некоторые виды входных данных. Поэтому вместо того, чтобы запускать один и тот же код для каждого теста, мы можем прикрепить к тестам функцию фикстуры, и она будет запускаться и возвращать данные в тест перед выполнением каждого теста.

Функция помечена как прибор с помощью —

@pytest.fixture

Тестовая функция может использовать прибор, указав имя прибора в качестве входного параметра.

Создайте файл test_div_by_3_6.py и добавьте в него приведенный ниже код

import pytest

@pytest.fixture
def input_value():
   input = 39
   return input

def test_divisible_by_3(input_value):
   assert input_value % 3 == 0

def test_divisible_by_6(input_value):
   assert input_value % 6 == 0

Здесь у нас есть функция фикстуры с именем input_value , которая предоставляет входные данные для тестов. Чтобы получить доступ к функции прибора, тесты должны упомянуть имя прибора в качестве входного параметра.

Pytest, пока выполняется тест, увидит имя прибора в качестве входного параметра. Затем он выполняет функцию фиксации, и возвращаемое значение сохраняется во входном параметре, который может использоваться тестом.

Выполните тест, используя следующую команду —

pytest -k divisible -v

Приведенная выше команда сгенерирует следующий результат —

test_div_by_3_6.py::test_divisible_by_3 PASSED
test_div_by_3_6.py::test_divisible_by_6 FAILED
============================================== FAILURES
==============================================
________________________________________ test_divisible_by_6
_________________________________________
input_value = 39
   def test_divisible_by_6(input_value):
>  assert input_value % 6 == 0
E  assert (39 % 6) == 0
test_div_by_3_6.py:12: AssertionError
========================== 1 failed, 1 passed, 6 deselected in 0.07 seconds
==========================

Однако этот подход имеет свои ограничения. Функция фикстуры, определенная в файле atest, имеет область действия только в тестовом файле. Мы не можем использовать этот прибор в другом тестовом файле. Чтобы сделать фикстуру доступной для нескольких тестовых файлов, мы должны определить функцию фикстуры в файле с именем conftest.py. conftest.py объясняется в следующей главе.

Pytest — Conftest.py

Мы можем определить функции фикстуры в этом файле, чтобы сделать их доступными для нескольких тестовых файлов.

Создайте новый файл conftest.py и добавьте в него приведенный ниже код —

import pytest

@pytest.fixture
def input_value():
   input = 39
   return input

Отредактируйте test_div_by_3_6.py, чтобы удалить функцию фиксации

import pytest

def test_divisible_by_3(input_value):
   assert input_value % 3 == 0

def test_divisible_by_6(input_value):
   assert input_value % 6 == 0

Создайте новый файл test_div_by_13.py

import pytest

def test_divisible_by_13(input_value):
   assert input_value % 13 == 0

Теперь у нас есть файлы test_div_by_3_6.py и test_div_by_13.py, использующие прибор, определенный в conftest.py .

Запустите тесты, выполнив следующую команду —

pytest -k divisible -v

Приведенная выше команда сгенерирует следующий результат —

test_div_by_13.py::test_divisible_by_13 PASSED
test_div_by_3_6.py::test_divisible_by_3 PASSED
test_div_by_3_6.py::test_divisible_by_6 FAILED
============================================== FAILURES
==============================================
________________________________________ test_divisible_by_6
_________________________________________
input_value = 39
   def test_divisible_by_6(input_value):
>  assert input_value % 6 == 0
E  assert (39 % 6) == 0
test_div_by_3_6.py:7: AssertionError
========================== 1 failed, 2 passed, 6 deselected in 0.09 seconds
==========================

Тесты будут искать крепеж в том же файле. Поскольку в файле не найден прибор, он проверит его в файле conftest.py. При его обнаружении вызывается метод fixture, и результат возвращается во входной аргумент теста.

Pytest — Тесты параметризации

Параметризация теста выполняется для запуска теста с несколькими наборами входов. Мы можем сделать это с помощью следующего маркера —

@pytest.mark.parametrize

Скопируйте приведенный ниже код в файл с именем test_multiplication.py

import pytest

@pytest.mark.parametrize("num, output",[(1,11),(2,22),(3,35),(4,44)])
def test_multiplication_11(num, output):
   assert 11*num == output

Здесь тест умножает входное значение на 11 и сравнивает результат с ожидаемым выходным значением. Тест имеет 4 набора входных данных, каждый из которых имеет 2 значения: одно — это число, которое нужно умножить на 11, а другое — ожидаемый результат.

Выполните тест, выполнив следующую команду —

Pytest -k multiplication -v

Приведенная выше команда сгенерирует следующий вывод:

test_multiplication.py::test_multiplication_11[1-11] PASSED
test_multiplication.py::test_multiplication_11[2-22] PASSED
test_multiplication.py::test_multiplication_11[3-35] FAILED
test_multiplication.py::test_multiplication_11[4-44] PASSED
============================================== FAILURES
==============================================
_________________ test_multiplication_11[3-35] __________________
num = 3, output = 35
   @pytest.mark.parametrize("num, output",[(1,11),(2,22),(3,35),(4,44)])
   def test_multiplication_11(num, output):
>  assert 11*num == output
E  assert (11 * 3) == 35
test_multiplication.py:5: AssertionError
============================== 1 failed, 3 passed, 8 deselected in 0.08 seconds
==============================

Pytest — Xfail / Пропустить тесты

В этой главе мы узнаем о тестах Skip и Xfail в Pytest.

Теперь рассмотрим следующие ситуации —

  • Тест не актуален в течение некоторого времени по ряду причин.
  • Внедряется новая функция, и мы уже добавили тест для этой функции.

В этих ситуациях у нас есть возможность пропустить тест или пропустить тесты.

Pytest выполнит тест xfailed, но он не будет считаться частью неудачной или пройденной проверки. Детали этих тестов не будут напечатаны, даже если тест не пройден (помните, что pytest обычно печатает данные о неудачном тесте). Мы можем xfail тесты, используя следующий маркер —

@pytest.mark.xfail

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

@pytest.mark.skip

Позже, когда тест становится актуальным, мы можем удалить маркеры.

Отредактируйте test_compare.py, мы уже должны включить маркеры xfail и skip —

import pytest
@pytest.mark.xfail
@pytest.mark.great
def test_greater():
   num = 100
   assert num > 100

@pytest.mark.xfail
@pytest.mark.great
def test_greater_equal():
   num = 100
   assert num >= 100

@pytest.mark.skip
@pytest.mark.others
def test_less():
   num = 100
   assert num < 200

Выполните тест, используя следующую команду —

pytest test_compare.py -v

После выполнения вышеуказанная команда сгенерирует следующий результат —

test_compare.py::test_greater xfail
test_compare.py::test_greater_equal XPASS
test_compare.py::test_less SKIPPED
============================ 1 skipped, 1 xfailed, 1 xpassed in 0.06 seconds
============================

Pytest — остановить тестовый пакет после N тестовых сбоев

В реальном сценарии, когда новая версия кода готова к развертыванию, она сначала развертывается в среде pre-prod / staging. Затем запускается набор тестов.

Код пригоден для развертывания в рабочей среде, только если набор тестов пройден. В случае сбоя теста, будь то один или несколько, код не готов к работе.

Поэтому, что если мы хотим остановить выполнение набора тестов вскоре после того, как n тестов не пройдено? Это можно сделать в pytest, используя maxfail.

Синтаксис для остановки выполнения набора тестов вскоре после того, как число тестов не выполнено, выглядит следующим образом:

pytest --maxfail = <num>

Создайте файл test_failure.py со следующим кодом.

import pytest
import math

def test_sqrt_failure():
   num = 25
   assert math.sqrt(num) == 6

def test_square_failure():
   num = 7
   assert 7*7 == 40

def test_equality_failure():
   assert 10 == 11

Все 3 теста не пройдут при выполнении этого файла теста. Здесь мы собираемся остановить выполнение теста после одной ошибки —

pytest test_failure.py -v --maxfail = 1
test_failure.py::test_sqrt_failure FAILED
=================================== FAILURES
=================================== _______________________________________
test_sqrt_failure __________________________________________
   def test_sqrt_failure():
   num = 25
>  assert math.sqrt(num) == 6
E  assert 5.0 == 6
E  + where 5.0 = <built-in function sqrt>(25)
E  + where <built-in function sqrt>= math.sqrt
test_failure.py:6: AssertionError
=============================== 1 failed in 0.04 seconds
===============================

В приведенном выше результате мы видим, что выполнение остановлено на один сбой.

Pytest — запускать тесты параллельно

По умолчанию pytest запускает тесты в последовательном порядке. В реальном сценарии набор тестов будет иметь несколько тестовых файлов, и каждый файл будет иметь набор тестов. Это приведет к большому времени выполнения. Чтобы преодолеть это, pytest предоставляет нам возможность проводить тесты параллельно.

Для этого нам нужно сначала установить плагин pytest-xdist.

Установите pytest-xdist, выполнив следующую команду —

pip install pytest-xdist

Теперь мы можем запустить тесты, используя синтаксис pytest -n <num>

pytest -n 3

-n <num> запускает тесты, используя несколько рабочих, здесь это 3.

У нас не будет большой разницы во времени, когда нужно выполнить всего несколько тестов. Однако важно, когда набор тестов большой.

Результаты выполнения теста в формате XML

Мы можем сгенерировать детали выполнения теста в xml-файле. Этот XML-файл в основном полезен в тех случаях, когда у нас есть инструментальная панель, которая проецирует результаты теста. В таких случаях xml может быть проанализирован, чтобы получить детали выполнения.

Теперь мы выполним тесты из test_multiplcation.py и сгенерируем xml, запустив

pytest test_multiplication.py -v --junitxml="result.xml"

Теперь мы видим, что файл result.xml генерируется со следующими данными:

<?xml version = "1.0" encoding = "utf-8"?>
<testsuite errors = "0" failures = "1"
name = "pytest" skips = "0" tests = "4" time = "0.061">
   <testcase classname = "test_multiplication"          
      file = "test_multiplication.py"
      line = "2" name = "test_multiplication_11[1-11]"
      time = "0.00117516517639>
   </testcase>
   
   <testcase classname = "test_multiplication"    
      file = "test_multiplication.py"
      line = "2" name = "test_multiplication_11[2-22]"
      time = "0.00155973434448">
   </testcase>

   <testcase classname = "test_multiplication" 
      file = "test_multiplication.py"
      line = "2" name = "test_multiplication_11[3-35]" time = "0.00144290924072">
      failure message = "assert (11 * 3) == 35">num = 3, output = 35

         @pytest.mark.parametrize("num,
         output",[(1,11),(2,22),(3,35),(4,44)])
            
         def test_multiplication_11(num, output):> 
         assert 11*num == output
         E assert (11 * 3) == 35

         test_multiplication.py:5: AssertionErro
      </failure>
   </testcase>
   <testcase classname = "test_multiplication" 
      file = "test_multiplication.py"
      line = "2" name = "test_multiplication_11[4-44]"
      time = "0.000945091247559">
   </testcase>
</testsuite>

Здесь, тег <testsuit> суммирует, что было 4 теста, и количество сбоев равно 1.

  • Тег <testcase> содержит подробную информацию о каждом выполненном тесте.

  • Тег <fail> содержит подробную информацию о неудачном тестовом коде.

Тег <testcase> содержит подробную информацию о каждом выполненном тесте.

Тег <fail> содержит подробную информацию о неудачном тестовом коде.

Pytest — Резюме

В этом уроке по Pytest мы рассмотрели следующие области:

  • Установка pytest ..
  • Определение тестовых файлов и тестовых функций.
  • Выполнение всех тестовых файлов с помощью pytest –v.
  • Выполнение определенного файла с помощью pytest <filename> -v.
  • Выполнить тесты, сопоставив подстроку pytest -k <substring> -v.
  • Выполнять тесты на основе маркеров pytest -m <marker_name> -v.
  • Создание светильников с использованием @ pytest.fixture.
  • conftest.py позволяет получить доступ к приборам из нескольких файлов.
  • Параметризация тестов с использованием @ pytest.mark.parametrize.
  • Xfailing тесты с использованием @ pytest.mark.xfail.
  • Пропуск тестов с использованием @ pytest.mark.skip.
  • Остановите выполнение теста при n ошибках, используя pytest —maxfail = <num>.
  • Параллельное выполнение тестов с использованием pytest -n <num>.
  • Генерация результатов xml с использованием pytest -v —junitxml = «result.xml».

Pytest — Заключение

Из этого туториала вы познакомились с платформой Pytest. Теперь вы должны начать писать тесты, используя pytest.

Как хорошая практика —