Якщо ви не повинні стверджувати про недійсність заяви твердження у виробничому коді? [зачинено]


32

Я бачив це питання, але у мене є ще кілька питань щодо використання assertключового слова. Я дебатував з кількома іншими кодерами щодо використання assert. Для цього випадку використання був метод, який може повернути нуль, якщо виконуються певні передумови. Код, який я написав, викликає метод, потім стверджує, що він не повертає нуль, і продовжує використовувати повернутий об'єкт.

Приклад:

class CustomObject {
    private Object object;

    @Nullable
    public Object getObject() {
        return (object == null) ? generateObject() : object;
    }
}

А тепер уявіть, що я використовую це так:

public void useObject(CustomObject customObject) {
    object = customObject.getObject();
    assert object != null;
    // Do stuff using object, which would throw a NPE if object is null.
}

Мені сказали, що я повинен видалити те assert, що вони ніколи не повинні використовуватися у виробничому коді, а використовуватись лише для тестування. Це правда?


19
За замовчуванням твердження вимкнено. Ви повинні чітко включити їх під час виконання через -eaабо еквівалент.
jarmod

Питання, пов’язані з інженерією програмного забезпечення : softwareengineering.stackexchange.com/q/137158/187318 (хоча воно досить старе, тому прийнята відповідь там все-таки рекомендує зовнішню бібліотеку, яка більше не потрібна з моменту впровадження Objects.requireNonNullв Java 8).
Халк

Заголовок цього питання є надто широким, але питання, викладене в тілі, набагато вужче, і відповідно до відповідей цілком обробляються допоміжні функції.
smci

Щоб було зрозуміло, ви запитуєте, чи повинен клієнтський код реалізувати ненульові перевірки / твердження на об'єктах. (Це відрізняється від запитання, чи повинен сам бібліотечний код гарантувати, що об'єкти не можуть бути нульовими, або стверджувати, що там немає).
smci

Відповіді:


19

Використовуйте Objects.requireNonNull(Object)для цього.

Перевіряє, чи вказана посилання на об'єкт не є нульовою. Цей метод призначений головним чином для перевірки параметрів у методах та конструкторах, [...]

У вашому випадку це було б:

public void useObject(CustomObject customObject) {
    object = customObject.getObject();
    Objects.requireNonNull(object);
    // Do stuff using object, which would throw a NPE if object is null.
}

Ця функція зроблена для зазначених вами цілей, тобто чітко позначте те, що не повинно бути недійсним; також у виробництві. Велика перевага полягає в тому, що ви переконайтеся, що знайдете нульові значення саме там, де вони не повинні виникати в першу чергу. У вас буде менше проблем з налагодженням помилок, викликаних нульовими значеннями, переданими десь там, де вони не повинні бути.

Ще однією перевагою є додаткова гнучкість щодо нульових перевірок на відміну від assert. Хоча assertце ключове слово для перевірки булевого значення, Objects.requireNonNull(Object)є функцією, і тому його можна вбудувати в код набагато більш гнучким і читабельним. Наприклад:

Foo foo = Objects.requireNonNull(service.fetchFoo());

// You cannot write it in on line.
Bar bar = service.fetchBar();
assert bar != null;
service.foo(Objects.requireNonNull(service.getBar()));

// You cannot write it in on line.
Bar bar = service.getBar();
assert bar != null;
service.foo(bar);

Майте на увазі, що Objects.requireNonNull(Object)це виключно для перевірки нуля, де assertузагальнено. assertмає в цьому плані дещо інші цілі, тобто насамперед тестування. Це потрібно ввімкнути, щоб ви могли ввімкнути його для тестування та відключити його для виробництва. У будь-якому разі не використовуйте його для виробничого коду. Це може уповільнити додаток із непотрібними та складними перевірами, які призначені для тестування. Використовуйте його для відокремлення перевірок, призначених лише для тестування, від перевірок, які також призначені для виробництва.

Перегляньте офіційну документацію, щоб отримати детальну інформацію про assert.


14
Хто і коли заявив, що стверджує спадщину? Будь-які посилання / посилання? Я не можу уявити жодного розумного розробника, який визначає "застарілий" інструмент, що дозволяє перевірити нульовий слід, який можна легко включити в тести та відключити у виробництві.
Дмитро Пісклов

Зважаючи на те, що ви можете вирішити під час виконання, чи працює агрегат, але ви не можете визначити під час виконання, чи NPE кинуто вимогоюNonNull, що ви маєте на увазі під поняттям "ви маєте більше контролю над ним, ніж з аргументом"?
Піт Кіркхем,

@DmitryPisklov Ви праві, я це відредагував. Це чудовий інструмент для тестування.
акузміних

2
@PeteKirkham Ви маєте рацію, контроль було неправильним словом для цього. Я більше говорив про гнучкість у синтаксисі. Крім того: тут можуть бути знайдені причини, чому ви хочете, щоб код кидав NPE.
акузміних

1
"не використовуйте його для виробничого коду. Це може сповільнити додаток" Це могло б, але це може бути варте того. У Java є вбудована перевірка часу виконання, щоб гарантувати, що ви ніколи не перевищуєте масив. Вони ввімкнуті навіть на виробничих розгортаннях, незважаючи на можливе покарання за продуктивність. Нам навіть вибір не з цього приводу. Не завжди неправильно проводити перевірку виробничого коду.
Макс Барраклу

22

Найголовніше, що потрібно пам’ятати про твердження, - це те, що вони можуть бути відключені, тому ніколи не припускайте, що вони будуть виконані.

Для зворотної сумісності JVM відключає перевірку тверджень за замовчуванням. Вони повинні бути чітко включені, використовуючи або аргумент командного рядка -enableassertions, або його скорочення -ea:

java -ea com.whatever.assertion.Assertion

Тож покладатися на них не надто добре.

Оскільки твердження не включені за замовчуванням, ви ніколи не можете припускати, що вони будуть виконані при використанні в коді. Тому завжди слід перевіряти нульові значення та порожні необов’язкові елементи, уникати використання тверджень для перевірки входів у загальнодоступний метод і замість цього використовувати неперевірений виняток ... Загалом усі перевірки роблять так, ніби твердження не було.


Очевидно, погана практика покладатися на них, але чи погана практика це використовувати взагалі?
Big_Bad_E

3
@Big_Bad_E Затвердження - це інструмент налагодження, який існує для того, щоб програма вийшла з ладу якомога раніше і як можна шуміше. Тоді завдання тестового набору - переконатись, що програма не може вийти з ладу, незважаючи на твердження . Ідея полягає в тому, що як тільки тестовий набір (він має 100% покриття, чи не ?! Звичайно, в цьому міркуванні є трохи ідеалізму, але це ідея.
cmaster - відновити моніку

11

Звичайно, те, що вам кажуть, - кричуща брехня. Ось чому.

Твердження відключені за замовчуванням, якщо ви просто виконаєте окремий jvm. Якщо вони відключені, вони мають нульовий слід, отже, вони не впливатимуть на ваше виробництво. Однак вони, мабуть, є вашими найкращими друзями при розробці та тестуванні вашого коду, і більшість тестуючих фреймворків увімкнення тверджень (JUnit робить), тому ваш код твердження виконується під час запуску одиничних тестів, що допомагає виявити будь-які потенційні помилки раніше (наприклад, Ви можете додати твердження для деяких перевірок кордону бізнес-логіки, і це допоможе виявити деякий код, який використовує невідповідні значення).

Однак, як свідчить інша відповідь, саме з цієї причини (вони не завжди ввімкнено) ви не можете покладатися на твердження, щоб зробити якісь життєві перевірки або (особливо!) Підтримувати будь-який стан.

Для цікавого прикладу того, як ви могли використовувати твердження, подивіться тут - в кінці файлу є метод, singleThreadedAccess()який викликається з заяви заяви в рядку 201 і чи є для того, щоб знайти будь-який потенційний багатопотоковий доступ у тестах.


4

Інші відповіді вже досить добре висвітлюють це питання, але є й інші варіанти.

Наприклад, Spring має статичний метод:

org.springframework.util.Assert.notNull(obj)

Є й інші бібліотеки зі своїми Assert.something()методами. Це також досить просто написати своє.

Однак майте на увазі, які винятки ви кинете, якщо це веб-сервіс. Попередній метод, згаданий, наприклад, кидає, IllegalArgumentExceptionякий за замовчуванням у Spring повертає 500.

Що стосується веб-сервісу, це часто не внутрішня помилка сервера і не повинна бути 500, а швидше 400, що є поганим запитом.


1
Якщо метод "Assert" не одразу руйнує процес, замість цього викидаючи якийсь виняток, то це не затвердження. Вся справа в assertтому, щоб негайно вийти з ладу з створеним основним файлом, щоб ви могли зробити посмертний улюблений улюблений налагоджувач прямо там, де умова була порушена.
cmaster - відновити моніку

Заяви не одразу завершують процес збоїв. Вони кидають Помилку, яка може бути спіймана. Неопрацьовані винятки приведуть до краху ваших тестів так само, як і помилок. Ви можете сперечатися з семантикою, якщо хочете. Якщо ви цього хочете, напишіть спеціальне твердження, яке видає помилку замість винятку. Справа в тому, що в переважній більшості випадків відповідні дії - це викид, а не помилка.
Крістофер Шнайдер

Ах, Java справді визначає assertкинути. Цікаво. Версія C / C ++ не робить цього. Це негайно викликає сигнал про те, що а) вбиває процес, і б) створює основний дамп. Це робиться з причини: Налагодити прості помилки такого твердження просто, оскільки у вас все ще доступна вся інформація про стек викликів. Визначення assertзамість цього викинути виняток, який може бути спійманий (не) навмисно програмно, перемагає мету, imho.
cmaster - відновити моніку

Так само, як у C та C ++ є деякі (або багато) дивні речі, такі є і на Java. Один з них є Throwable. Їх завжди можна зловити. Це добре чи ні, я не знаю. Я думаю, це залежить. Багато програм Java - це веб-сервіси, і це було б небажано збиватися практично з будь-якої причини, тому майже все потрапляє та реєструється. Однією з чудових речей є стек стека, і цього, як правило, достатньо, щоб діагностувати причину виключення чи помилки самостійно.
Крістофер Шнайдер

3

Використовувати твердження вільно, коли це робить, допомагає виявити помилки програмування, тобто помилки.

Не використовуйте assrt, щоб зловити щось, що може логічно статися, тобто неправильно відформатований вхід. Використовуйте assert лише тоді, коли помилку неможливо усунути.

Не вкладайте ніякої виробничої логіки в код, який працює під час перевірки твердження. Якщо ваше програмне забезпечення добре написано, це тривіально правда, але якщо це не так, у вас можуть бути тонкі побічні ефекти та різна загальна поведінка з увімкненими та відключеними твердженнями.

Якщо у вашої компанії є "код тестування" та "виробничий код", які роблять те саме, але різні бази коду (або різні етапи редагування), виходьте звідти і ніколи не повертайтеся. Намагаючись виправити цей рівень некомпетентності, мабуть, марна трата вашого часу. Якщо ваша компанія не ставить заяву про ствердження за межами коду тестів, будь ласка, скажіть їм, що твердження вимкнено у виробничій збірці, а якщо їх немає, виправлення цієї помилки є вашим першим пріоритетом.

Значення тверджень полягає саме в тому, щоб використовуватись всередині бізнес-логіки, а не лише тестового набору. Це дозволяє легко виконати безліч тестів високого рівня, які не повинні явно перевіряти багато речей, щоб пройти великі шматки вашого коду та запустити всі ці твердження. У декількох моїх проектах типові тести навіть насправді нічого не стверджували, вони просто наказали зробити розрахунок на основі конкретного вкладу, і це призвело до перевірки сотень тверджень і проблем, які можна знайти навіть у крихітних фрагментах логіки.


2

Ви можете використовувати АСТЕР в будь-який час. Дебати - це коли їх використовувати. Наприклад у посібнику :

  • Не використовуйте твердження для перевірки аргументів у публічних методах.
  • Не використовуйте твердження для виконання будь-якої роботи, необхідної вашій програмі для правильної роботи.

4
Хороші правила. Але відповідь було б краще, якщо додати деякі причини.
cmaster - відновити моніку
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.