Введення в специфіку Assert та його різновиди
Ексклюзивні ІТ-новини, огляди та інтерв'ю
Введення в специфіку Assert та його різновиди
Assert – це спеціальна конструкція, що дозволяє перевіряти припущення про значення довільних даних у довільному місці програми. Ця конструкція може автоматично сигналізувати при виявленні некоректних даних, що зазвичай призводить до аварійного завершення програми із зазначенням місця виявлення некоректних даних.
Дивна, на перший погляд, конструкція — може завалити програму в невідповідний момент. Який у ній сенс? Давайте разом подумаємо, що станеться, якщо під час виконання програми в якийсь момент часу деякі дані програми стали некоректними і ми не завалили відразу програму, а продовжили її роботу, як ні в чому не бувало.

Програма може ще довго працювати після цього без жодних видимих помилок. А може будь-якої миті часу в майбутньому «завалитися» сама з відомої тільки їй причини. Або раптом накачати вам повний вінчестер контенту згей-порносайтів.
Assert'и доступні у багатьох мовах програмування, включаючи java, c#, c та python.
Які види assert'ів бувають?
Assert'и дозволяють відловлювати помилки у програмах на етапі компіляції або під час виконання. Перевірки на етапі компіляції не такі важливі — здебільшого їх можна замінити на аналогічні перевірки під час виконання програми. Іншими словами, assert'и на етапі компіляції є нічим іншим, як синтаксичним цукром.
Тому надалі під assert'ами матимемо на увазі лише перевірки під час виконання програми
Assert'и можна розділити на такі класи.
#1. Перевірка вхідних аргументів на початку функції
Якщо знайдено неприпустиме значення будь-якого аргументу, отже, десь поруч із місцем виклику цієї функції може бутибаги.
Важливо розуміти, що аргументи функції можуть бути неявними.

Наприклад, при виклику методу класу на функцію неявно передається покажчик на об'єкт даного класу (aka this і self ). Також функція може звертатися до даних, оголошених у глобальній області видимості, або даних з області видимості лексичного замикання. Ці аргументи також бажано перевіряти за допомогою assert'ів при вході в функцію.
#2. Перевірка даних, з якими функція функція, перед виходом із цієї функції
Якщо некоректні дані виявлено цьому етапі, то код цієї функції може містити баги.
Результат функції може бути неявним. Наприклад, функція може модифікувати дані, на які посилаються (прямо чи опосередковано) аргументи функції. Також функція може модифікувати дані з глобальної області видимості або області видимості лексичного замикання.

Коректність цих даних бажано перевіряти перед виходом із функції.
#3. Перевірка даних, з якими працює функція, всередині коду функції
Якщо в середині функції виявляються некоректні дані, то баги можуть бути в районі цієї перевірки.
Коли і де варто використовувати assert'и?
Відповідь проста — використовуйте assert'и завжди і скрізь, де вони хоч трохи можуть здатися корисними. Адже вони суттєво спрощують локалізацію багів у коді. Навіть перевірка результатів виконання очевидного коду може виявитися корисною при подальшому рефакторингу, після якого код може стати не настільки очевидним і в нього запросто може закраситися баг.
Не бійтеся, що велика кількістьassert'ів погіршить ясність коду та сповільнить виконання вашої програми. Assert'и візуально виділяються із загального коду та несуть важливу інформацію про припущення, на основі яких працює цей код.
Більшість мов програмування підтримують відключення assert'ів або на етапі компіляції, або під час виконання програми, тому вони мають мінімальний вплив на продуктивність програми. Зазвичай assert'и залишають включеними під час розробки та тестування програм, але відключають у реліз-версіях програм.
Якщо програма написана у найкращих традиціях ОВП, або за допомогою enterprise методології, то assert'и взагалі можна не відключати — продуктивність навряд чи зміниться :)
Коли можна обійтись без assert'ів?
Коли я тільки дізнався про існування assert'ів, мої програми стали містити 100500 assert'ів, багато з яких багато разів дублювали один одного. З часом кількість assert'ів у моєму коді стала зменшуватися. Наступні правила дозволили багаторазово зменшити кількість assert'ів у моїх програмах без істотного погіршення ефективності вилову багів:
• Можна уникати дублюючих перевірок вхідних аргументів шляхом розміщення їх лише у функціях, які безпосередньо працюють з даним аргументом. Тобто. якщо функція foo() не працює з аргументом, лише передає їх у функцію bar() , можна опустити перевірку цього аргументу функції foo() , т.к. вона продубльована перевіркою аргументу функції bar() .
• Можна опускати assert'и на неприпустимі значення, які гарантовано призводять до краху програми безпосередньо поблизу даних assert'ів, тобто. якщо за крахом програми можна швидко визначити місцезнаходження бага. До таких assert'ів можна віднести перевірки покажчика на NULLперед його розіменуванням та перевірки на нульове значення дільника перед розподілом. Ще раз повторюся - такі перевірки можна опускати лише тоді, коли середовище виконання гарантує крах програми у цих випадках.

Цілком можливо, що існують інші способи, що дозволяють зменшити кількість assert'ів без погіршення ефективності вилову багів.
Коли не можна використовувати assert'и?
Т.к. assert’и можуть бути видалені на етапі компіляції або під час виконання програми, вони не повинні змінювати поведінку програми. Якщо в результаті видалення assert'а поведінка програми може змінитися, то це явна ознака неправильного використання assert'а.
Таким чином, всередині assert'а не можна викликати функції, що змінюють стан програми або зовнішнього оточення програми
Наприклад, наступний код неправильно використовує assert'и:
Очевидно, що дані можуть виявитися незахищеними при вимкнених assert'ах.
Щоб виправити цю помилку, потрібно зберігати результат виконання функції у тимчасовій змінній, після чого використовувати цю змінну всередині assert'а:
Якщо write() повертає 0, це зовсім не означає, що в нашій програмі є баг. Якщо assert'и в програмі будуть відключені, то помилка запису може залишитися непоміченою, що може призвести до сумних результатів. Тому assert() тут не підходить.
Тут краще підходить нормальна обробка помилки.
Я програмую на JavaScript. У ньому немає assert'ів. Що мені робити?
У деяких мовах програмування відсутня явна підтримка assert'ів. За бажання вони легко можуть бути там реалізовані, дотримуючись наступного «патерну проектування»:
Грамотно розставлені assert'и спрощують автоматизоване тестування коду,т.к. тестуюча програма може опустити перевірки, що дублюють assert'и в коді програми. Такі перевірки зазвичай становлять суттєву частку всіх перевірок у програмі, що тестує.