Что такое pytest и как его использовать? Или как новичку начать автоматизировать тестирование?

от автора

Привет, друзья!

Сегодня я хочу рассказать о pytest и о том, как с ним начать работать. Сам когда-то начинал и столкнулся со множеством сложностей, но теперь я готов поделиться своим опытом.

Pytest

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

Давайте разберёмся, что умеет этот фреймворк, на примере простых конструкций, которые часто советуют тестировщикам.

Вот код, с которым мы будем работать. Писать и запускать его будем в GigaIDE.

# myProject/cashReceipt/cashReceipt.py  class Receipt:     """Класс для создания чека и наполнения его данными о покупках"""     __instance = None     __receipt_id = 0      def __new__(cls, *args, **kwargs):         cls.__instance = super().__new__(cls)         cls.__receipt_id += 1         return cls.__instance      def __init__(self, name, surname, patronymic, date, time, total):         self.name = name         self.surname = surname         self.patronymic = patronymic         self.date = date         self.time = time         self.total = total         self.items = []      @classmethod     def get_receipt_id(cls):         return cls.__receipt_id      @property     def id(self):         return self.get_receipt_id()      def add_item(self, item):         self.items.append(item)      def print_receipt(self):         print("Чек №", self.id)         print("Покупатель:", self.name, self.surname, self.patronymic)         print("Дата:", self.date)         print("Время:", self.time)         print("Итого:", self.total)         for item in self.items:             print(item.name, item.price, item.quantity)   class Item:     """Класс для создания товара, его цены за штуку или 100г и количество"""     def __init__(self, name, price, quantity):         self.name = name         self.price = price         self.quantity = quantity   if __name__ == "__main__":     receipt = Receipt("Иван", "Иванов", "Иванович", "12.12.2020", "12:00", 100)     receipt.add_item(Item("Молоко", 50, 2))     receipt.add_item(Item("Хлеб", 30, 1))     receipt.print_receipt()     receipt2 = Receipt("Петр", "Петров", "Петрович", "12.12.2020", "12:00", 100)     receipt2.add_item(Item("Молоко", 50, 2))     receipt2.add_item(Item("Хлеб", 30, 1))     receipt2.print_receipt()     

Первый запуск Pytest

Есть несколько способов запустить программу на Python. Один из них:

pytest cachReceipt/cachReceipt.py

Магия в том, что при таком запуске интерпретация кода меняется, и результат запуска тоже меняется:

============================== test session starts ============================== platform win32 -- Python 3.12.0, pytest-8.3.4, pluggy-1.5.0 rootdir: C:\Users\Всеволод\IdeaProjects\pyTest collected 0 items  ============================= no tests ran in 0.01s =============================

Тесты не найдены.

Второй запуск Pytest

Для второго запуска нам нужно создать файл, в котором мы реализуем проверку по всем правилам pytest. Для этого нужно импортировать cashReceipt.py в файл с проверкой. В этом примере я использовал универсальный способ импорта, который не зависит от IDE и зависит только от ОС (различия только в том, как будут прописаны пути).

# myProject/tests/testCashReceipt.py  import pytest import sys  sys.path.append("/home/user/IdeaProjects/myProject/cashReceipt")  from cashReceipt.cashReceipt import Receipt, Item   receipt = Receipt("Иван", "Иванов", "Иванович", "12.12.2020", "12:00", 100) receipt.add_item(Item("Молоко", 50, 2)) receipt.add_item(Item("Хлеб", 30, 1)) items = receipt.items   @pytest.mark.parametrize("item", receipt.items) def test_cashReceipt(item):     assert item.price <= 100     

Чтобы такой импорт заработал, нужно настроить файл _ _init_ _.py, который делает из обычного каталога python-пакет.

# myProject/cashReceipt/__init__.py from cashReceipt import * 

Теперь становится понятно, как можно тестировать объекты с разными параметрами на входе. Мы можем протестировать (покрыть) все атрибуты и методы класса, который создаёт чеки. А если мы будем использовать несколько классов (например, класс «кошелёк»), то сможем делать интеграционные проверки (проверять взаимодействия разных классов между собой).

Конструкция @pytest.mark.parametrize освоена. Более подробно о ней в интернете много доступной информации.

Результат запуска:

============================= test session starts ============================= collecting ... collected 1 item  testCashReceipt.py::test_cash_receipt[receipt0] PASSED                   [100%]  ============================== 1 passed in 0.01s ==============================

Но код в листинге выше не в стиле pytest, ему не хватает фикстур.

Третий запуск Pytest

Давайте создадим рядом с файлом тестирования файл conftest.py. Этот файл будет хранить объекты, которые мы тестируем.

# myProject/tests/conftest.py  import pytest import sys sys.path.append("/home/user/IdeaProjects/myProject/cashReceipt")  from cashReceipt.cashReceipt import Receipt, Item   @pytest.fixture def receipt(request):     return Receipt(*request.param)   @pytest.fixture def item(request):     return Item(*request.param) 

И уберём лишний код из testCashReceipt

# myProject/tests/testCashReceipt.py  import pytest   @pytest.mark.parametrize("receipt", [("Иван", "Иванов", "Иванович", "12.12.2020", "12:00", 100)], indirect=True) def test_cash_receipt(receipt):     assert receipt is not None   @pytest.mark.parametrize("item", [("Молоко", 50, 2), ("Хлеб", 30, 1)], indirect=True) def test_cash_item(item):     assert item is not None 

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

А вот и результат нашего третьего запуска:

============================= test session starts ============================= collecting ... collected 3 items  testCashReceipt.py::test_cash_receipt[receipt0] PASSED                   [ 33%] testCashReceipt.py::test_cash_item[item0] PASSED                         [ 66%] testCashReceipt.py::test_cash_item[item1] PASSED                         [100%]  ============================== 3 passed in 0.01s ==============================

Итог

В результате, всего за три запуска мы поняли, как сделать проверки на pytest, сохранить принципы ООП и всё структурировать. Класс!

Бонус

Структура проекта:

myProject |-- cashReceipt |   |-- __init__.py |   |-- cashReceipt.py |-- tests     |-- __init__.py     |-- conftest.py     |-- testCashReceipt.py


ссылка на оригинал статьи https://habr.com/ru/articles/876570/


Комментарии

Добавить комментарий

Ваш адрес email не будет опубликован. Обязательные поля помечены *