Модуль 7 · Урок 26 із 58
Test design: boundaries, tables і negative cases
Велика кількість tests не гарантує сильне покриття поведінки. Найцінніші cases походять із contract: equivalence classes, межі, invalid inputs, state transitions і наслідки failure. Таблиця cases допомагає бачити пропуски до написання коду.
Безпечна практика модуля 7
Пакет містить лише synthetic fixtures, unittest, Mock/spec_set, TemporaryDirectory та локальний subprocess. Жодних персональних чи production-даних, secrets, мережевих викликів або зовнішніх залежностей.
Equivalence classes скорочують безмежний input space
Для percentage contract 0..100 не треба тестувати кожне integer. Виділіть classes: valid middle, lower boundary, upper boundary, below lower, above upper, wrong type. Один representative із class корисний лише коли behavior справді однакова для всього class.
Boundary value analysis дивиться по обидва боки правила
cases = (
(0, 100),
(1, 99),
(99, 1),
(100, 0),
)
for percent, expected in cases:
with self.subTest(percent=percent):
self.assertEqual(final_price(100, percent), expected)Typical off-by-one ховається біля < проти <=. Для interval перевіряють exactly boundary, nearest value всередині й nearest value назовні. Для dates, lengths і pagination той самий принцип потребує domain-specific unit.
subTest зберігає case identity
subTest запускає iterations в одному test method і повідомляє parameters невдалої iteration. Це доречно для одного contract із compact table. Якщо cases мають різний setup або різні business reasons, окремі test methods дадуть яснішу diagnosis.
invalid = (-1, 101)
for value in invalid:
with self.subTest(value=value):
with self.assertRaisesRegex(ValueError, "0..100"):
final_price(100, value)Negative test перевіряє і signal, і side effects
Недостатньо побачити будь-яку exception. Перевірте exact type, safe message, незмінність caller input і відсутність output artifact. Інакше function може частково записати report, а потім чесно впасти — для user це corrupted outcome.
with self.assertRaisesRegex(InputError, "row 2"):
import_records(source, target)
self.assertFalse(target.exists())
self.assertEqual(source.read_text(encoding="utf-8"), original)Test oracle має бути незалежним від implementation
Якщо expected result обчислюється копією того самого algorithm, обидві версії можуть мати однакову помилку. Для small cases пишіть exact expected values вручну, використовуйте trusted property або alternate representation. Snapshot корисний лише після review і не повинен перетворювати update snapshots на автоматичне прийняття змін.
Mutation-oriented review перевіряє силу suite
Поставте запитання: чи впаде test, якщо <= стане <, sort зникне, exception заміниться success, input почне mutate-итися? Не потрібно вручну псувати production branch; достатньо review against plausible defects. Green suite, що переживає очевидну неправильну зміну, має слабкий oracle.
Definition of Done
- Contract розкладено на equivalence classes і boundaries до коду tests.
- Case table має readable IDs або subTest parameters.
- Invalid inputs перевіряють exact failure та відсутність partial state.
- Expected value не дублює implementation algorithm.
- Suite реагує на plausible off-by-one, missing-sort і mutation defects.
Методичні джерела
Урок, сценарії, пояснення й вправи створені SEOWORK. Посилання ведуть лише на офіційну документацію Python.
Закріпіть матеріал уроку
Три сценарні питання. Для зарахування уроку потрібно дати щонайменше дві правильні відповіді.