Урок розвиває конфігурацію попереднього відео у трьох напрямках: генерує невалідні тестові дані через Faker, замінює нетипізований dictionary на immutable dataclass і виносить повторюваний логін у function-scoped pytest fixture. Рефакторинг виконується лише після появи конкретного дублювання або незручності.
Примітка: конспект укладено за автоматичними українськими субтитрами; назви API та помилок нормалізовано за контекстом відео.
Після цього уроку ви зможете
Генерувати test data через Faker і окремо задавати умову, яка гарантує невалідність негативного сценарію.
Описувати test configuration як typed @dataclass(frozen=True).
Використовувати function-scoped fixture лише в тестах, яким потрібна передумова логіну.
Рефакторити тільки після підтвердженого дублювання або помилки contract.
Захардкоджений невалідний пароль замінюється згенерованим значенням. Мета — не повноцінне покриття класів еквівалентності чи граничних значень, а різні екземпляри даних у повторних запусках.
Додається бібліотека Faker і викликається генератор пароля заданої довжини. Залежність також має бути зафіксована у requirements.txt.
Що змінилося після запису
Faker password може бути валідним
АктуальноПоточний Faker password provider керує довжиною й категоріями символів, але не перевіряє policy target application. Для гарантованого negative test потрібне явне порушення відомої вимоги, наприклад довжина 10 при minimum 12.
Перевірено 2026-07-31
Уточнення
Random не гарантує invalid data
Назва змінної invalid_password не створює негативний contract. Спершу зафіксуйте конкретну password policy, а потім згенеруйте значення, яке гарантовано порушує одну з її умов.
Термін
Faker password provider
Faker().password() генерує рядок заданої довжини та дозволяє керувати категоріями символів, але не знає password policy конкретної системи.
Перший виклик виправляється після звірки з API: треба створити екземпляр Faker(), а вже потім викликати його provider, наприклад fake.password(length=10). Повторні запуски демонструють різні значення.
Змінна називається invalid_password, щоб тест явно пояснював роль даних. Практична межа рандомізації: при падінні варто логувати або зберігати згенероване значення чи seed, інакше дефект може бути важко відтворити.
Приклад коду
Typed configuration і пароль, невалідний за явною policy
from dataclasses import dataclass
from faker import Faker
@dataclass(frozen=True)
class Config:
login_url: str
email: str
config = Config(login_url="https://example.test/login", email="student@example.test")
generated_password = Faker().password(length=10)
assert config.login_url.endswith("/login")
assert len(generated_password) == 10
assert len(generated_password) < 12 # Invalid for this example's explicit minimum-length policy.
print(config.email)
Приклад створює frozen configuration і генерує пароль довжиною 10, який гарантовано порушує задану в прикладі minimum-length policy 12.
Dictionary конфігурації незручний: IDE не підказує дозволені ключі, а помилки в рядках знаходяться лише під час виконання. Замість нього створюється @dataclass(frozen=True) з полями URL, email і password.
frozen=True забороняє звичайне переприсвоєння полів після створення об’єкта. Це добре відповідає конфігурації одного тестового запуску: значення зчитали на старті й далі лише використовують.
Уточнення
`frozen=True` — контрольований contract, не security boundary
Python documentation прямо називає frozen behavior імітацією: generated methods блокують звичайне assignment, але це не механізм захисту secret або hostile code.
Термін
frozen dataclass
Параметр frozen=True додає захист від звичайного присвоєння полів і піднімає FrozenInstanceError; документація описує це як emulated immutability, а не абсолютну незмінність Python object.
Після зміни типу старе звернення config["email"] падає з TypeError: object is not subscriptable. Це очікуваний сигнал: dataclass використовує атрибути, тому код змінюється на config.email, config.password, config.login_url.
Результат — автодоповнення, безпечніше перейменування через IDE й читабельніший контракт fixture. Type hints не роблять Python статично типізованим самі по собі, але дають IDE та аналізаторам достатньо інформації, щоб знаходити частину помилок раніше.
У двох тестах повторюється авторизація, тому з’являється fixture login_user. Fixture розглядається як механізм підготовки та, за потреби, очищення стану до/після тесту.
Для логіну обирається scope function: підготовка виконується окремо перед кожним тестом, який явно запитує fixture. Це ізолює сценарії й не поширює авторизований стан на тести, яким він не потрібен.
Термін
function-scoped fixture
function є default scope pytest fixture: fixture знищується наприкінці кожного тесту, який її використовує.
Fixture додається параметром лише до двох сценаріїв, яким потрібен логін. Інший тест залишається без неї. Така явність показує залежності сценарію без прихованого глобального setup.
Повторювана назва цільового проєкту переноситься в TARGET_PROJECT на рівні модуля. Це предметне значення, яке справді використовується в кількох місцях; окрема абстракція для одиничного рядка не створюється.
Автор перевіряє usages helper-функцій через IDE перед видаленням або перенесенням. Підкреслення та Find Usages допомагають не «спростити» один тест ціною поломки сусіднього.
Підхід уроку: почати з прямого тесту, побачити повторення, винести рівно спільну частину, знову запустити тести. Поточних dataclass і fixtures достатньо; додатковий framework або Page Object на цьому кроці ще не потрібні.
Перед комітом перевіряється, що Faker має зафіксовану версію в requirements.txt, а всі тести проходять після переходу на dataclass і fixture. Повідомлення коміту описує обидві зміни: типізовану конфігурацію та генерацію тестових даних.
Підсумковий стан: конфігурація незмінна й має явний тип, негативний сценарій використовує згенерований пароль, а логін виконується fixture лише там, де потрібен. Це зменшує дублювання без передчасного переходу до складнішої архітектури.