Является ли слишком много утверждений кода запахом?

19

Я действительно влюбился в модульное тестирование и TDD - я заражен тестом.

Тем не менее, модульное тестирование обычно используется для общедоступных методов. Хотя иногда мне приходится проверять некоторые предположения-утверждения и в частных методах, потому что некоторые из них «опасны», и рефакторинг не может помочь в дальнейшем. (Я знаю, что инфраструктуры тестирования позволяют тестировать частные методы).

Так что стало моей привычкой, что первая и последняя строка частного метода являются утверждениями.

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

Может ли кто-то думать о слишком многих утверждениях как о запахе кода?

Флоренс Целай
источник
1
эти утверждения включены или выключены в выпуске?
JK.
Я работаю в основном с Java, где утверждения должны быть включены вручную.
Флоренц Целай
Только что нашёл это «Утверждения повышают производительность за счет отслеживания ошибок» programmers.stackexchange.com/a/16317/32342
Флоренс Целай
как вы определяете "слишком много"?
Хайлем
@haylem Есть «слишком много» утверждений, если другой программист читает код и говорит: «Я устал от этих утверждений».
Флоренс Целай

Ответы:

26

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

Хранить их как утверждения лучше, чем просто использовать комментарии, потому что они активно проверяют предположения при каждом запуске.

Oleksi
источник
2
Очень хороший момент!
Флоренс Целай
1
Утверждения являются документацией, но это не делает их дублирование законным. Напротив, это даже вызывает подозрения в отношении качества тестов, если кажется, что одни и те же утверждения необходимы более одного раза.
Ям Маркович
1
@YamMarcovic Я думаю, что это приемлемо, потому что два «дублированных» фрагмента кода служат двум различным и различным целям. Я думаю, что полезно иметь их в обоих местах, несмотря на дублирование. Наличие утверждений в методе неоценимо для документации, и они, очевидно, необходимы для тестирования.
Олекси
2
Утверждения в коде для меня дополняют утверждения модульного тестирования. У вас не будет 100% покрытия тестами, а утверждения в коде помогут вам быстрее сузить неудачные юнит-тесты.
Себастьян Гейгер
15

Похоже, вы пытаетесь сделать дизайн-по-контракту вручную.

Делать DbC - хорошая идея, но вы должны, по крайней мере, рассмотреть вопрос о переходе на язык, который поддерживает его изначально (например, Eiffel ), или, по крайней мере, использовать систему контрактов для вашей платформы (например, Microsoft Code Contracts для .NET).это довольно красиво, возможно, самая сложная структура контракта, даже более мощная, чем Eiffel). Таким образом, вы можете лучше использовать силу контрактов. Например, используя существующую платформу, вы можете отобразить контракты в своей документации или IDE (например, контракты кода для .NET показаны в IntelliSense и, если у вас VS Ultimate, даже могут статически проверяться автоматическим средством проверки теорем во время компиляции, пока вы печатаете, также во многих Java-контрактных средах есть доклеты JavaDoc, которые автоматически извлекают контракты в вашу документацию по JavaDoc API).

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

Итак, вкратце: если вы действительно делаете DbC, даже если вы этого не знаете, тогда эти утверждения совершенно хороши.

Йорг Миттаг
источник
4

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

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

Я не думаю, что вы должны утверждать возвращаемые параметры (если у вас явно нет инварианта, который вы хотите, чтобы читатель понял). Это также работа юниттестов.

Лично мне не нравится assertутверждение в Java, так как они могут быть отключены, и тогда это ложная защита.


источник
1
+1 За «Мне не нравится утверждение assert в Java, так как их можно отключить, и тогда это ложная защита».
Флоренс Целай
3

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

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

Тестирование выходных условий (например, состояния объекта или этого возвращаемого значения! = Null) должно выполняться во внутренних методах (например, в закрытых методах). Если выходные данные передаются напрямую из закрытого метода в выходные данные открытого метода без дополнительных вычислений или изменений внутреннего состояния, то тестирование условий вывода открытого метода может быть излишним.

Однако я согласен с Олекси в том, что избыточность может повысить удобочитаемость, а также может повысить удобство сопровождения (если прямое назначение или возврат больше не будет иметь место в будущем).

Дэнни Варод
источник
4
Если пользователь (вызывающая сторона) открытого интерфейса может вызвать условие ошибки или недопустимое условие аргумента, это должно быть полностью обработано обработкой ошибок. Это означает форматирование сообщения об ошибке, которое имеет смысл для конечного пользователя, сопровождается кодом ошибки, ведением журнала и попытками восстановления данных или восстановления в нормальное состояние. Подстановка правильной обработки ошибок с утверждениями сделает код менее надежным и трудным для устранения неполадок.
Rwong
Утверждения могут (и во многих случаях должны) включать регистрацию и выдачу исключений (или кодов ошибок в C--).
Дэнни Варод
3

Трудно быть не зависящим от языка в этом вопросе, поскольку детали того, как реализуются утверждения и «правильная» обработка ошибок / исключений, имеют отношение к ответу. Вот мои 0,02 доллара, основанные на моих знаниях Java и C ++.

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

Утверждения в публичных методах обычно лучше избегать. Вы все равно должны делать такие вещи, как проверка того, что контракт метода не нарушен, но если это так, то вы должны генерировать исключения с подходящей типизацией со значимыми сообщениями, возвращать состояние, где это возможно, и т. Д. (Что @rwong называет «полным правильная обработка ошибок ").

Вообще говоря, вы должны использовать только утверждения, чтобы помочь вашей разработке / отладке. Вы не можете предполагать, что у тех, кто использует ваш API, даже при включении вашего кода будут включены утверждения. Хотя они действительно помогают в документировании кода, часто есть лучшие способы документировать одни и те же вещи (например, документация по методам, исключения).

vaughandroid
источник
2

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

Также не все программисты идеальны - опять же, утверждения означают, что один из этих «промахов» не будет распространяться слишком далеко.

Не стоит недооценивать влияние этих утверждений (и других проверок на допущения) на снижение стоимости обслуживания.

mattnz
источник
0

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

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

Также рассмотрите обслуживание вашего тестового проекта. Что происходит, когда вы добавляете новый параметр или выходные данные слегка подправляются, придется ли вам возвращаться обратно через кучу тестов и изменять утверждение или добавлять утверждение? И, в зависимости от вашего кода, вам может понадобиться гораздо больше настроек, чтобы убедиться, что все утверждения пройдены.

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

bwalk2895
источник
0

Тем не менее, модульное тестирование используется для общедоступных методов.

Если ваша инфраструктура тестирования допускает средства доступа, тогда неплохо бы использовать модульное тестирование частных методов (IMO).

Может ли кто-то думать о слишком многих утверждениях как о запахе кода?

Я делаю. Утверждения в порядке, но ваша первая цель должна состоять в том, чтобы сценарий был даже невозможен ; не только то, что этого не происходит.

Telastyn
источник
-2

Это плохая практика.

Вы не должны тестировать публичные / приватные методы. Вы должны проверить класс в целом. Просто убедитесь, что у вас есть хорошее освещение.

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

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

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

Ям Маркович
источник
-1 Юнит-тестирование класса в целом может быть опасным, так как в итоге вы тестируете более чем одну вещь за тест. Это делает действительно хрупкие тесты. Вы должны тестировать один фрагмент поведения одновременно, что часто соответствует тестированию только одного метода на тест.
Олекси
Также в отношении «это говорит о том, что автор кода действительно не знал, что он делает [...] вы делаете». Будущие разработчики могут не знать, что делает конкретный метод. В этом случае наличие предварительных и последующих условий в форме утверждений может иметь неоценимое значение для понимания фрагмента кода.
Олекси
2
@Oleksi Хорошее тестирование - это правильные сценарии использования, а не утверждение каждой мельчайшей несущественной детали. Избыточные утверждения действительно являются запахом кода из-за неясных намерений и являются еще одним плохим примером защитного программирования.
Ям Маркович