🏠 Домашнее задание: Summary session 6

← К оглавлению урока

Примечание. Урок 12 — это summary-урок (повторение). Отдельного LMS-задания для него нет. Вместо этого здесь: разбор ДЗ из лекции (POM Checkout), самопроверка знаний, настройка окружения и проверка в VS Code.

⚡ ДЗ из лекции: checkout через POM

Открыть saucedemo.com → авторизоваться → добавить 3 товара (Backpack, Bolt T-Shirt, Onesie) → корзина → Checkout → заполнить форму → проверить Total = $58.29.

Использовать POM: LoginPage, InventoryPage, CartPage, CheckoutPage.

ДЗ из лекции: Checkout через POM

Задание из лекции Summary session 6. Написать автоматизированный тест с использованием POM, который выполняет:
  1. Открыть сайт: https://www.saucedemo.com/
  2. Авторизоваться как standard_user
  3. Добавить в корзину: Sauce Labs Backpack, Sauce Labs Bolt T-Shirt, Sauce Labs Onesie
  4. Перейти в корзину
  5. Нажать Checkout
  6. Заполнить форму: имя, фамилия, почтовый индекс
  7. Прочитать итоговую стоимость (Total)
  8. Проверить, что итоговая сумма равна $58.29
Требования: использовать POM; вынести все локаторы и методы в классы страниц; тест должен запускаться без предварительной подготовки данных.

Подготовка окружения

# PowerShell — создание и активация venv
python -m venv venv
.\venv\Scripts\Activate.ps1

# Установка зависимостей
pip install selenium webdriver-manager pytest
# Структура проекта для ДЗ
saucedemo_checkout/
├── pages/
│   ├── __init__.py
│   ├── base_page.py
│   ├── login_page.py
│   ├── inventory_page.py
│   ├── cart_page.py
│   └── checkout_page.py
└── tests/
    ├── __init__.py
    ├── base_test.py
    └── test_checkout.py

Пошаговое решение

Шаг 1: BasePage

# pages/base_page.py
from selenium.webdriver.support.ui import WebDriverWait
from selenium.webdriver.support import expected_conditions as EC


class BasePage:
    def __init__(self, driver):
        self.driver = driver
        self.wait = WebDriverWait(driver, 10)

    def find(self, locator):
        return self.wait.until(EC.presence_of_element_located(locator))

    def click(self, locator):
        self.wait.until(EC.element_to_be_clickable(locator)).click()

    def type_text(self, locator, text):
        field = self.find(locator)
        field.clear()
        field.send_keys(text)

    def get_text(self, locator):
        return self.find(locator).text

    def find_all(self, locator):
        return self.wait.until(EC.presence_of_all_elements_located(locator))

Шаг 2: CheckoutPage

# pages/checkout_page.py
from selenium.webdriver.common.by import By
from pages.base_page import BasePage


class CheckoutPage(BasePage):
    FIRST_NAME   = (By.ID, "first-name")
    LAST_NAME    = (By.ID, "last-name")
    POSTAL_CODE  = (By.ID, "postal-code")
    CONTINUE_BTN = (By.ID, "continue")
    TOTAL_LABEL  = (By.CLASS_NAME, "summary_total_label")

    def fill_checkout_form(self, first_name, last_name, postal_code):
        self.type_text(self.FIRST_NAME, first_name)
        self.type_text(self.LAST_NAME, last_name)
        self.type_text(self.POSTAL_CODE, postal_code)
        self.click(self.CONTINUE_BTN)

    def get_total_price(self):
        return self.get_text(self.TOTAL_LABEL)

Шаг 3: BaseTest с CheckoutPage

# tests/base_test.py
import pytest
from selenium import webdriver
from selenium.webdriver.chrome.service import Service as ChromeService
from webdriver_manager.chrome import ChromeDriverManager
from pages.login_page import LoginPage
from pages.inventory_page import InventoryPage
from pages.cart_page import CartPage
from pages.checkout_page import CheckoutPage


class BaseTest:
    @pytest.fixture(scope="class", autouse=True)
    def setup(self):
        self.driver = webdriver.Chrome(
            service=ChromeService(ChromeDriverManager().install())
        )
        self.driver.maximize_window()
        self.driver.get("https://www.saucedemo.com/")

        self.login_page     = LoginPage(self.driver)
        self.inventory_page = InventoryPage(self.driver)
        self.cart_page      = CartPage(self.driver)
        self.checkout_page  = CheckoutPage(self.driver)

        yield

        self.driver.quit()

Шаг 4: Тест checkout

# tests/test_checkout.py
from tests.base_test import BaseTest


class TestCheckout(BaseTest):
    def test_checkout_total_price(self):
        # 1. Открытие и авторизация
        self.login_page.open()
        self.login_page.success_login("standard_user", "secret_sauce")

        # 2. Добавление товаров в корзину
        self.inventory_page.add_item_to_cart("Sauce Labs Backpack")
        self.inventory_page.add_item_to_cart("Sauce Labs Bolt T-Shirt")
        self.inventory_page.add_item_to_cart("Sauce Labs Onesie")

        # 3. Переход в корзину
        self.inventory_page.go_to_cart()

        # 4. Оформление заказа
        self.cart_page.proceed_to_checkout()
        self.checkout_page.fill_checkout_form("John", "Doe", "12345")

        # 5. Проверка итоговой стоимости
        total_price = self.checkout_page.get_total_price()
        assert total_price == "Total: $58.29", f"Итоговая сумма неверна: {total_price}"
Примечание: Метод get_total_price() возвращает полный текст элемента .summary_total_label. Это может быть "Total: $58.29", поэтому лучше проверять через assert "$58.29" in total_price, если текст элемента содержит префикс.

Запуск и проверка в VS Code

Запуск из терминала (PowerShell)

# Запуск теста
pytest tests/test_checkout.py -v

# Ожидаемый вывод:
# PASSED tests/test_checkout.py::TestCheckout::test_checkout_total_price

Запуск с отладкой (F5 в VS Code)

Создайте файл .vscode/launch.json:

// .vscode/launch.json
{
    "version": "0.2.0",
    "configurations": [
        {
            "name": "pytest: checkout",
            "type": "debugpy",
            "request": "launch",
            "module": "pytest",
            "args": ["tests/test_checkout.py", "-v"],
            "cwd": "${workspaceFolder}",
            "console": "integratedTerminal"
        }
    ]
}
  1. Откройте tests/test_checkout.py
  2. Поставьте точку останова на строке assert total_price == ...
  3. Нажмите F5 — запустится отладка
  4. В панели Variables проверьте значение total_price

Самопроверка

После успешного выполнения вы должны понимать:

  • Почему POM лучше, чем тесты с «голыми» локаторами
  • Как структурировать проект: pages/, tests/, conftest.py
  • Зачем нужен BasePage и как от него наследоваться
  • Как работает BaseTest с scope="class" и autouse=True
  • Почему важен __init__.py в каждой директории-пакете

Связь с другими разделами урока

  • Теория — принципы POM, BasePage, BaseTest, структура проекта
  • Примеры — полные коды LoginPage, InventoryPage, CartPage
  • Ошибки — StaleElementReferenceException, грязное состояние, отсутствие __init__.py