Модуль 7 · Урок 26 із 58

Test design: boundaries, tables і negative cases

Велика кількість tests не гарантує сильне покриття поведінки. Найцінніші cases походять із contract: equivalence classes, межі, invalid inputs, state transitions і наслідки failure. Таблиця cases допомагає бачити пропуски до написання коду.

boundariessubTestnegative casestest table

Безпечна практика модуля 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.

Valid0, 1, 50, 99, 100.
Invalid-1, 101.
TypeTrue, “10”, None.
EffectReturn, exception, no side effect.

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.

Coverage: line coverage показує, що рядок виконувався, але не доводить якість assertion, коректність boundaries чи negative side-effect contract.

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.

Практична перевірка · урок 26 з 47

Закріпіть матеріал уроку

Три сценарні питання. Для зарахування уроку потрібно дати щонайменше дві правильні відповіді.

1. Percentage дозволений inclusive від 0 до 100. Який compact set найкраще ловить off-by-one на обох межах?
2. Навіщо до test code формулювати equivalence classes для великого input space?
3. Коли subTest доречніший за чотири майже однакові test methods у unittest suite?