Стоит ли утверждать не нуль с помощью оператора assert в производственном коде? [закрыто]

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, что они никогда не должны использоваться в производственном коде, только для тестирования. Это правда?

Big_Bad_E
источник
19
По умолчанию утверждения отключены. Вы должны явно включить их во время выполнения через -eaили эквивалентный.
Джармод
Связанный вопрос по программной инженерии: softwareengineering.stackexchange.com/q/137158/187318 (хотя он довольно старый, поэтому в принятом ответе по-прежнему рекомендуется использовать внешнюю библиотеку, которая больше не требуется с момента появления Objects.requireNonNullв Java 8).
Халк
Название этого вопроса слишком широкое, но вопрос, указанный в теле, гораздо более узкий, и, согласно ответам, полностью обрабатывается вспомогательными функциями.
SMCI
Чтобы было ясно, вы спрашиваете, должен ли клиентский код реализовывать ненулевые проверки / утверждения на объектах. (Это отличается от вопроса, должен ли сам код библиотеки гарантировать, что объекты не могут быть нулевыми, или утверждать, что это так).
SMCI
1
Возможный дубликат: когда утверждения должны оставаться в рабочем коде?
Питер Мортенсен

Ответы:

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.

akuzminykh
источник
14
Кто и когда объявил, что заявляет о своем наследии? Любые ссылки / ссылки? Я не могу себе представить, чтобы какой-нибудь здравомыслящий разработчик определял «устаревший» инструмент, позволяющий проводить проверки с минимальными затратами, которые можно легко включить в тестах и ​​отключить в производстве.
Дмитрий Писклов
Учитывая, что вы можете решить во время выполнения, работает ли assert, но вы не можете определить во время выполнения, выбрасывает ли NPE параметр requireNonNull, что вы подразумеваете под «у вас больше контроля над ним, чем с помощью assert»?
Пит Киркхэм
@DmitryPisklov Вы правы, я редактировал это. Это отличный инструмент для тестирования.
Акузьминых
2
@PeteKirkham Вы правы, контроль был неправильным словом для этого. Я больше говорил о гибкости в синтаксисе. Кроме того: здесь могут быть найдены причины, по которым вы хотели бы, чтобы код генерировал NPE.
Акузьминых
1
«Не используйте его для производственного кода. Это может замедлить работу приложения». Может, но оно того стоит. Java имеет встроенные проверки во время выполнения, чтобы гарантировать, что вы никогда не переполните массив. Они включены даже в производственных развертываниях, несмотря на возможное снижение производительности. Нам даже не дают выбора по этому поводу. Не всегда неправильно иметь проверки времени выполнения в рабочем коде.
Макс Барракло
22

Самое важное, что нужно помнить об утверждениях, это то, что они могут быть отключены, поэтому никогда не предполагайте, что они будут выполнены.

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

java -ea com.whatever.assertion.Assertion

Поэтому не стоит полагаться на них.

Поскольку утверждения не включены по умолчанию, вы никогда не можете предполагать, что они будут выполнены при использовании в коде. Поэтому вы всегда должны проверять наличие нулевых значений и пустых необязательных параметров, избегать использования утверждений для проверки входных данных в общедоступном методе и вместо этого использовать исключение без проверки ... В общем, выполняйте все проверки, как если бы утверждения не было.

jeprubio
источник
Очевидно, плохая практика полагаться на них, но является ли это плохой практикой, чтобы использовать это вообще?
Big_Bad_E
3
@Big_Bad_E Утверждения - это инструмент отладки, позволяющий проваливать программу как можно раньше и с максимальной шумностью. В этом случае набор тестов должен гарантировать, что программа не сможет завершиться неудачей, несмотря на утверждения . Идея состоит в том, что после того, как тестовый набор (он имеет 100% охват, не так ли?!?) Выполняется без сбоев, он сохраняет для удаления утверждения, поскольку они не могут сработать в любом случае. Конечно, в этом рассуждении есть немного идеализма, но это идея.
cmaster - восстановить монику
11

Конечно, то, что вам говорят, это вопиющая ложь. Вот почему

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

При этом, как следует из другого ответа, именно по этой причине (они не всегда включены) вы не можете полагаться на утверждения для выполнения некоторых жизненно важных проверок или (особенно!) Поддержания какого-либо состояния.

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

Дмитрий Писклов
источник
4

Другие ответы уже достаточно хорошо охватывают это, но есть и другие варианты.

Например, у Spring есть статический метод:

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

Есть и другие библиотеки со своими Assert.something()методами. Это также довольно просто написать свой собственный.

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

В случае веб-службы это часто не внутренняя ошибка сервера, и она должна быть не 500, а 400, что является неправильным запросом.

Кристофер Шнайдер
источник
1
Если метод «Утвердить» не приводит к немедленному завершению процесса, а вместо этого выдает какое-то исключение, тогда это не утверждение. Весь смысл в assertтом, чтобы получить немедленный сбой с созданным основным файлом, чтобы вы могли сделать посмертную работу с вашим любимым отладчиком прямо там, где было нарушено условие.
cmaster - восстановить монику
Утверждения не сразу завершают процесс. Они выдают ошибку, которая может быть поймана. Необработанные исключения будут сбивать ваши тесты так же, как и ошибки. Вы можете утверждать семантику, если хотите. Если вы хотите, напишите пользовательское утверждение, которое выдает ошибку вместо исключения. Дело в том, что в подавляющем большинстве случаев правильное действие - выбросить исключение, а не ошибку.
Кристофер Шнайдер
Ах, Java действительно определяет assertбросить. Интересно. Версия C / C ++ не делает этого. Это немедленно поднимает сигнал, что а) убивает процесс, и б) создает дамп ядра. Это делается по причине: отладить такой сбой утверждения очень просто, потому что у вас все еще есть вся информация о стеке вызовов. Определение assertвместо того, чтобы генерировать исключение, которое затем может быть намеренно поймано (не) программно, побеждает цель, imho.
cmaster - восстановить монику
Так же, как есть некоторые (или многие) странные вещи в C и C ++, есть некоторые в Java. Одним из них является Throwable. Их всегда можно поймать. Хорошо это или нет, я не знаю. Я думаю, это зависит Многие Java-программы являются веб-службами, и было бы нежелательно, чтобы они зависали практически по любой причине, поэтому почти все регистрируется и регистрируется. Одна из замечательных вещей - трассировка стека, и этого обычно достаточно, чтобы самостоятельно диагностировать причину исключения или ошибки.
Кристофер Шнайдер
3

Использование утверждений свободно, когда это делает, помогает обнаруживать ошибки программирования, то есть ошибки.

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

Не помещайте никакую производственную логику в код, который запускается, когда проверяется утверждение. Если ваше программное обеспечение хорошо написано, это тривиально, но если это не так, у вас могут быть незначительные побочные эффекты и другое общее поведение с включенными и отключенными утверждениями.

Если в вашей компании «тестирующий код» и «производственный код» выполняют одно и то же, но на разных основах кода (или на разных этапах редактирования), уходите оттуда и никогда не возвращайтесь. Попытка исправить этот уровень некомпетентности, вероятно, пустая трата вашего времени. Если ваша компания не помещает никаких утверждений assert вне кода тестов, пожалуйста, скажите им, что утверждения отключены в производственной сборке, а если нет, исправление этой ошибки теперь является вашим первым приоритетом.

Значение утверждений должно точно использоваться в бизнес-логике, а не только в наборе тестов. Это облегчает создание многих высокоуровневых тестов, которые не должны явно тестировать многие вещи, чтобы пройти через большие фрагменты вашего кода и вызвать все эти утверждения. В некоторых из моих проектов типичные тесты даже ничего не утверждали, они просто предписывали выполнить вычисления на основе конкретного ввода, и это привело к проверке сотен утверждений и обнаружению проблем даже в крошечных кусочках логики.

Kafein
источник
2

Вы можете использовать Assert в любое время. Приходят дебаты, когда использовать. Например, в руководстве :

  • Не используйте утверждения для проверки аргументов в открытых методах.
  • Не используйте утверждения для выполнения любой работы, которая требуется вашему приложению для правильной работы.
Gatusko
источник
4
Хорошие правила Но ответ будет лучше с некоторыми добавленными причинами.
cmaster - восстановить монику