← Python мануфактура

Після цього уроку ви зможете

Конспект і таймкоди

0:00

Fixture scopes і задача параметризації

Pytest fixture може мати scope session, package, module, class або function. Чим ширший scope, тим рідше створюється ресурс: session fixture — один раз на весь test run, function fixture — окремо для кожного тесту. Код до yield виконує setup, а після yield — teardown відповідного scope.

Ціль уроку — перетворити один негативний login test на параметризований. Тіло сценарію залишається одним, а різні пари email/password та читабельні case IDs передаються як дані.

Термін

fixture scope

Межа кешування й teardown fixture: function, class, module, package або session. Залежність може використовувати fixture того самого або ширшого scope, але не вужчого.

4:00

Ітеративний prompting і перша таблиця даних

LLM спочатку отримує вузьку задачу: додати pytest.mark.parametrize та згенерувати невалідні значення через Faker. Після запуску результат уточнюється окремими кроками. Такий цикл «малий prompt — diff — запуск — наступне уточнення» краще локалізує помилки, ніж один запит на повний рефакторинг fixtures, даних і тестів одночасно.

Пари даних виносяться з decorator у окрему змінну. Це скорочує тіло тесту, дає таблиці предметну назву й дозволяє за потреби перенести test data в окремий модуль. Важливо не генерувати випадкові значення без мети: кожен case має представляти конкретний клас поведінки.

10:00

Класи еквівалентності, boundary values та IDs

Таблиця розширюється через equivalence partitioning і boundary value analysis: валідний формат незареєстрованого email, відсутня локальна або domain-частина, пробіли, порожній пароль, занадто короткі чи довгі значення. Окремо LLM пропонує рядки, схожі на XSS і SQL injection payloads.

Ці payloads є лише вхідними прикладами, а не доказом захищеності: security check повинен мати конкретний expected result і виконуватися в безпечному тестовому середовищі. Для кожної параметризованої ітерації задається id, щоб у pytest output і CI report було видно не номер набору, а зміст failed case.

Запуск виявляє як помилки з Faker/API, так і server-side rate limit. Це корисний сигнал: параметризація збільшує кількість однотипних запитів, тому test data й темп запуску мають враховувати обмеження системи.

Термін

parameter ID

Читабельний identifier конкретного parameter set, заданий через ids, callable або pytest.param(..., id=...). Він з'являється у collection і failure output.

Приклад коду

Параметризовані cases з IDs

import pytest

LOGIN_CASES = [
    pytest.param("", "secret", id="missing-email"),
    pytest.param("user@example.com", "", id="missing-password"),
]

@pytest.mark.parametrize(("email", "password"), LOGIN_CASES)
def test_invalid_login_data(email: str, password: str) -> None:
    assert not email or not password

Дані відокремлені від test body, а кожна ітерація має предметний ID.

Очікуваний результат: Дві ітерації проходять і відображають IDs missing-email та missing-password.

Потрібно: pytest

Практика

Перевірка fixture isolation

  1. Створіть параметризовані negative login cases з IDs.
  2. Запустіть їх із function-scoped BrowserContext.
  3. Окремо спробуйте shared page із явним cleanup.
  4. Додайте check, що authorization state попередньої ітерації не доступний наступній, і порівняйте час запуску.

Результат: Є вимірювання speed trade-off і test, який падає при state leakage.

16:00

Від plugin fixtures до власного життєвого циклу

Стандартні pytest-playwright fixtures добре ізолюють тести: нові context/page створюються автоматично, а cleanup виконує plugin. Для великої таблиці негативних логінів це створює помітні накладні витрати, тому у відео будується власний lifecycle: один Playwright/browser instance на ширший scope і спеціальні fixtures для clean app, logged-in app та shared page.

Ключова ієрархія залежностей: Playwright instance запускає browser; browser створює context; context створює page. Scope залежної fixture не може бути ширшим за ресурс, від якого вона залежить. yield повинен закрити рівно ті ресурси, які fixture створила.

Це optimization з ціною: повторне використання page/context послаблює ізоляцію. Починати безпечніше зі стандартних function-scoped fixtures, а reuse додавати лише після виміряного bottleneck і разом із перевіреним cleanup.

Увага

Shared page послаблює default isolation

Ручне очищення cookies і local storage не дорівнює новому BrowserContext. Якщо reuse справді потрібен після вимірювання, fixture повинна мати явний contract стану й окрему regression check на leakage.

Уточнення

Default pytest-playwright lifecycle уже reuse browser

Офіційний plugin тримає playwright, browser_type і browser на session scope, але context, page і new_context — на function scope. Shared context/page з уроку є custom ownership, а не default plugin fixture lifecycle.

Термін

BrowserContext isolation

Окремий incognito-like browser profile з власними cookies, local storage і session storage. Playwright використовує новий context як надійну межу ізоляції тесту.

24:00

Shared page та очищення стану

Перші версії fixture щоразу відкривають нову вкладку або неправильно закривають page. Реалізацію уточнюють так, щоб параметризовані cases працювали на тій самій page, але перед новою ітерацією очищали cookies і local storage та поверталися у відомий початковий стан.

Простого localStorage.clear() недостатньо для універсальної ізоляції: стан також може бути в session storage, IndexedDB, service workers або server-side session. Тому shared context має сенс лише тоді, коли перелік станів відомий і cleanup перевірений. Новий BrowserContext залишається надійнішою межею ізоляції.

30:00

Перевикористання fixtures в інших тестах

Після прискорення login cases нові fixtures пробують застосувати до решти suite. Для сценаріїв, яким уже потрібен авторизований користувач, planned fixture має один раз виконати login і віддати готову page. Для негативного login test, навпаки, потрібна неавторизована shared page.

Автоматичний рефакторинг змінює більше тестів, ніж очікувалося, і частково плутає їх передумови. Це демонструє практичне правило: fixture називається за гарантованим станом (anonymous_page, authenticated_page), а migration робиться по одному behavior з повторним запуском, а не одним глобальним LLM-edit.

36:00

Діагностика конфлікту fixture lifecycle

Після рефакторингу suite падає через змішування plugin-managed fixtures та вручну створеного sync Playwright instance. Додатково частина launch/context options дублюється в dictionary і кількох fixtures, що робить конфігурацію непослідовною.

Рішення у відео — перейти на один спосіб володіння lifecycle: custom fixtures запускають Playwright і browser, а конфігурація збирається в одному місці. Частину pytest-playwright параметрів прибирають, бо custom launcher їх уже не читає. Загальний висновок: не можна одночасно очікувати, що plugin і власний код керуватимуть тим самим browser lifecycle.

44:00

Base URL, skip markers і відновлення suite

Наступні failures виявляються не проблемою браузера, а конфігурацією: fixture використовує неправильний base URL. Після виправлення route тести знову доходять до цільової сторінки.

Також виправляється неправильний виклик pytest.skip() на рівні module collection. Для декларативного пропуску тесту потрібен @pytest.mark.skip або module-level marker; інакше pytest може пропустити весь модуль або зупинити collection не так, як очікувалося.

Негативний login test відкриває sign-in route напряму. Перехід через home page і клік Login не є частиною його контракту, лише додає час і ще одну можливу причину падіння.

50:00

Швидкість, rate limit і межі reuse

Після прямої навігації та reuse page параметризований тест виконується швидше, але все одно впирається в rate limit. У демонстрації додається двосекундна пауза. Це придатна тимчасова діагностика, але стабільне рішення має узгодити навантаження з test environment: контрольовані test accounts, documented quota, backoff або окремий seam для form validation.

Наприкінці ще раз простежується lifecycle: browser може жити всю session, context — module або function, page — відповідно до вимог тесту. Чим довше живе ресурс, тим вища швидкість і тим більший ризик state leakage. Scope обирають не за принципом «найширший = найкращий», а за найдовшою безпечною межею.

Увага

Для HTTP 429 немає універсальної двосекундної паузи

429 означає забагато запитів за визначений сервером період. Response може містити Retry-After; quota й backoff визначає test environment, а не pytest або Playwright.

56:00

Commit і підсумок практики

Перед комітом переглядаються видалені plugin parameters, custom fixtures, requirements і параметризовані cases. У підсумку кожна ітерація має читабельний ID, test data винесені з тіла сценарію, а browser reuse виконується через явний fixture graph.

Практика після уроку — додати власні cases до таблиці, дати їм змістовні IDs, намалювати залежності fixtures і перевірити setup/teardown кожного scope. Окрема перевірка має довести, що попередня ітерація не залишає авторизацію або client-side state наступній.

Джерела та додаткові матеріали

  • pytest: How to use fixtures ↗pytest · перевірено 2026-07-31

    Офіційний contract scopes, dependencies і teardown fixtures.

  • pytest: Parameter IDs ↗pytest · перевірено 2026-07-31

    Актуальні способи задати читабельні IDs параметризованих cases.

  • Playwright: Isolation ↗Microsoft Playwright · перевірено 2026-07-31

    Межа BrowserContext і ризики cleanup-based isolation.

  • RFC 6585: 429 Too Many Requests ↗RFC Editor · перевірено 2026-07-31

    Відокремлює HTTP rate-limit contract від довільної fixed delay у тесті.

  • Playwright Pytest Plugin Reference ↗Microsoft Playwright · перевірено 2026-07-31

    Підтверджує session-scoped browser і function-scoped context/page fixtures.