Должны ли мы писать тесты для наших геттеров и сеттеров или это слишком сложно?
Должны ли быть написаны модульные тесты для геттеров и сеттеров?
Ответ 1
Я бы сказал, нет.
@Будем говорить, что вы должны стремиться к 100% охвату кода, но, на мой взгляд, это опасное отвлечение. Вы можете написать модульные тесты, которые имеют 100% -ный охват, и при этом абсолютно ничего не тестировать.
Тестирование модулей проводится для проверки поведения вашего кода выразительным и значимым образом, а getters/seters - только средство для достижения цели. Если вы тестируете, используйте геттеры/сеттеры для достижения своей цели тестирования "реальной" функциональности, тогда это достаточно хорошо.
Если, с другой стороны, ваши геттеры и сеттеры делают больше, чем просто получают и устанавливают (т.е. они являются сложными методами), то да, они должны быть протестированы. Но не пишите случай unit test, чтобы проверить геттер или сеттеры, что пустая трата времени.
Ответ 2
Громкое ДА с TDD
Примечание. Этот ответ продолжает вызывать недовольство, хотя потенциально это плохой совет. Чтобы понять почему, взгляните на его младшую сестру ниже.
Спорные хорошо, но я бы утверждать, что тот, кто отвечает "нет" на этот вопрос отсутствует фундаментальное понятие TDD.
Для меня ответ - да, если вы следите за TDD. Если нет, то нет правдоподобного ответа.
DDD в TDD
Часто упоминается, что TDD обладает основными преимуществами.
- Защита
- Обеспечение того, что код может измениться,, но не его поведение.
- Это позволяет очень важную практику рефакторинга.
- Вы получаете этот TDD или нет.
- Дизайн
- Вы указываете, что что-то должно делать, как оно должно вести себя до его реализации.
- Это часто означает более обоснованные решения о реализации.
- Документация
- Набор тестов должен служить в качестве спецификации (требований) документации.
- Использование тестов для этой цели означает, что документация и реализация всегда находятся в согласованном состоянии - изменение одного означает изменение другого. Сравните с сохранением требований и дизайна в отдельном текстовом документе.
Отдельная ответственность от реализации
Как программисты, ужасно заманчиво думать об атрибутах как о чем-то значимом, а о геттерах и сеттерах - как о каких-то издержках.
Но атрибуты - это детали реализации, а сеттеры и геттеры - это контрактный интерфейс, который фактически заставляет программы работать.
Гораздо важнее записать, что объект должен:
Разрешить своим клиентам изменять свое состояние
и
Разрешить своим клиентам запрашивать его состояние
затем, как на самом деле хранится это состояние (для которого атрибут является наиболее распространенным, но не единственным способом).
Тест, такой как
(The Painter class) should store the provided colour
важен для документации части TDD.
Тот факт, что возможная реализация является тривиальной (атрибутной) и не несет никакой защиты, должен быть неизвестен при написании теста.
Отсутствие круговой инженерии...
Одной из ключевых проблем в мире разработки систем является отсутствие круговой инженерии 1 - процесс разработки системы раздроблен на отдельные подпроцессы, артефакты которых (документация, код) часто несовместимы.
1 Броди, Майкл Л. "Джон Милопулос: шить семена концептуального моделирования". Концептуальное моделирование: основы и приложения. Springer Berlin Heidelberg, 2009. 1-9.
... и как TDD решает это
Документация является частью TDD, которая обеспечивает постоянную согласованность спецификаций системы и ее кода.
Сначала проектируй, потом реализуй
В рамках TDD мы сначала пишем неудачный приемочный тест, а затем пишем код, который позволяет им пройти.
В BDD более высокого уровня мы сначала пишем сценарии, а затем заставляем их проходить.
Почему вы должны исключить сеттеры и геттеры?
Теоретически, в рамках TDD один человек может написать тест, а другой - реализовать код, который делает его успешным.
Так что спросите себя:
Должен ли человек, пишущий тесты для класса, упомянуть геттеры и сеттеры.
Поскольку методы получения и установки являются открытым интерфейсом для класса, ответ, очевидно, да, иначе не будет никакого способа установить или запросить состояние объекта. Однако, способ сделать это не обязательно путем тестирования каждого метода в отдельности, подробнее см. мой другой ответ.
Очевидно, что если вы сначала напишите код, ответ может быть не таким четким.
Ответ 3
Рой Ошерове в своей знаменитой книге "Искусство модульного тестирования" говорит:
Свойства (getters/seters в Java) являются хорошими примерами кода, который обычно не содержит никакой логики и не требует тестирования. Но будьте осторожны: как только вы добавите какую-либо проверку внутри свойства, вы захотите проверить, проверена ли логика.
Ответ 4
tl;dr: Да, вы должны, и с OpenPojo это тривиально.
Вы должны провести некоторую проверку в ваших методах получения и установки, так что вам следует это проверять. Например,
setMom(Person p)
не должен позволять устанавливать кого-либо моложе себя в качестве своей матери.Даже если вы этого не делаете сейчас, скорее всего, так и будет в будущем, тогда это будет полезно для регрессионного анализа. Если вы хотите разрешить матерям установить
null
, вам следует пройти тест, который, если кто-то изменит это позже, укрепит ваши предположения.Распространенная ошибка -
void setFoo( Object foo ){ foo = foo; }
, где она должна бытьvoid setFoo( Object foo ){ this.foo = foo; }
. (В первом случае записываетсяfoo
- это параметр, а не полеfoo
в объекте).Если вы возвращаете массив или коллекцию, вам следует проверить, будет ли получатель выполнять защитные копии данных, переданных в установщик, перед возвратом.
В противном случае, если у вас есть самые основные сеттеры/геттеры, то их модульное тестирование добавит, возможно, около 10 минут максимум для каждого объекта, так какова потеря? Если вы добавите поведение, у вас уже есть скелетный тест, и вы получите этот регрессионный тест бесплатно. Если вы используете Java, у вас нет оправданий, так как есть OpenPojo. Существует существующий набор правил, которые можно включить, а затем отсканировать с ними весь проект, чтобы убедиться, что они последовательно применяются в вашем коде.
Из своих примеров:
final PojoValidator pojoValidator = new PojoValidator();
//create rules
pojoValidator.addRule( new NoPublicFieldsRule () );
pojoValidator.addRule( new NoPrimitivesRule () );
pojoValidator.addRule( new GetterMustExistRule () );
pojoValidator.addRule( new SetterMustExistRule () );
//create testers
pojoValidator.addTester( new DefaultValuesNullTester () );
pojoValidator.addTester( new SetterTester () );
pojoValidator.addTester( new GetterTester () );
//test all the classes
for( PojoClass pojoClass : PojoClassFactory.getPojoClasses( "net.initech.app", new FilterPackageInfo() ) )
pojoValidator.runValidation( pojoClass );
Ответ 5
Да, но не всегда в изоляции
Позвольте мне уточнить:
Что такое unit test?
От эффективной работы с устаревшим кодом 1:
Термин unit test имеет долгую историю разработки программного обеспечения. Общий большинство концепций модульных тестов - это идея, что они являются испытаниями в изоляции отдельных компонентов программного обеспечения. Какие компоненты? Определение варьируется, но при модульном тестировании нас обычно интересуют самые атомные поведенческие единицы системы. В процедурном коде единицы часто являются функциями. В объектно-ориентированном коде единицы являются классами.
Обратите внимание, что с ООП, где вы найдете геттеры и сеттеры, единица - это класс, не обязательно отдельные методы.
Что такое хороший тест?
Все требования и тесты следуют форме Логика Хора:
{P} C {Q}
Где:
-
{P}
является предварительным условием (данным) -
C
- это условие триггера (когда) -
{Q}
- это постусловие (тогда)
Затем появляется максим:
Тестирование, а не реализация
Это означает, что вы не должны проверять, как C
достигает пост-состояния, вы должны убедиться, что {Q}
является результатом C
.
Когда дело доходит до ООП, C
- это класс. Поэтому вы не должны тестировать внутренние эффекты, а только внешние эффекты.
Почему бы не тестировать геттеры и сеттеры bean в изоляции
Getters и seters могут включать в себя некоторую логику, но так долго эта логика не имеет внешнего эффекта - делая их bean аксессуарами 2), тест должен будет заглянуть внутрь объекта и тем самым не только нарушают инкапсуляцию, но также проверяют на реализацию.
Поэтому вы не должны тестировать геттеры и сеттеры bean изолированно. Это плохо:
Describe 'LineItem class'
Describe 'setVAT()'
it 'should store the VAT rate'
lineItem = new LineItem()
lineItem.setVAT( 0.5 )
expect( lineItem.vat ).toBe( 0.5 )
Хотя если setVAT
выдаст исключение, соответствующий тест будет подходящим, так как теперь есть внешний эффект.
Как вы должны тестировать геттеры и сеттеры?
Фактически нет смысла изменять внутреннее состояние объекта, если такое изменение не влияет на внешность, даже если такой эффект наступает позже.
Таким образом, тест для сеттеров и геттеров должен быть связан с внешним эффектом этих методов, а не с внутренними.
Например:
Describe 'LineItem class'
Describe 'getGross()'
it 'should return the net time the VAT'
lineItem = new LineItem()
lineItem.setNet( 100 )
lineItem.setVAT( 0.5 )
expect( lineItem.getGross() ).toBe( 150 )
Вы можете подумать:
Подождите секунду, мы тестируем
getGross()
здесь неsetVAT()
.
Но если setVAT()
неисправность в том, что тест все равно должен потерпеть неудачу.
1 Feathers, M., 2004. Эффективно работает с устаревшим кодом. Prentice Hall Professional.
2 Martin, R.C., 2009. Clean code: руководство по гибкому программному мастерству. Образование Пирсона.
Ответ 6
Хотя есть обоснованные причины для свойств, существует общая идея объектно-ориентированного дизайна, заключающаяся в том, что разоблачение состояния участника через свойства является плохим дизайном. Статья Роберта Мартина об Открытом Закрытом Принципе раскрывает это, заявляя, что Свойства поощряют связь и поэтому ограничивают возможность закрытия класса от модификации - если вы изменяете свойство, все потребители этого класса также должны измениться. Он квалифицирует, что подвергая переменные-члены не обязательно плохой дизайн, это может быть просто плохой стиль. Однако, если свойства доступны только для чтения, меньше шансов на злоупотребления и побочные эффекты.
Наилучший подход, который я могу предоставить для модульного тестирования (и это может показаться странным), заключается в том, чтобы сделать как можно больше свойств защищенными или внутренними. Это предотвратит соединение, не поощряя писать глупые тесты для геттеров и сеттеров.
Есть очевидные причины, по которым должны использоваться свойства чтения/записи, такие как свойства ViewModel, которые привязаны к полям ввода и т.д.
Более практично, модульные тесты должны обеспечивать функциональность с помощью общедоступных методов. Если код, который вы тестируете, использует эти свойства, вы получаете бесплатное покрытие кода. Если окажется, что эти свойства никогда не будут подсвечиваться с помощью покрытия кода, существует очень сильная возможность:
- Вам не нужны те тесты, которые косвенно используют свойства
- Свойства не используются
Если вы пишете тесты для геттеров и сеттеров, вы получаете ложное ощущение охвата и не сможете определить, действительно ли свойства используются функциональным поведением.
Ответ 7
Если цикломатическая сложность геттера и/или сеттера равна 1 (обычно они есть), тогда ответ не будет, вы должны "т.
Итак, если у вас нет SLA, для которого требуется 100% -ное покрытие кода, не беспокойтесь и сосредоточьтесь на тестировании важного аспекта вашего программного обеспечения.
P.S. Не забывайте различать геттеры и сеттеры даже в таких языках, как С#, где свойства могут казаться одинаковыми. Сложность сеттера может быть выше, чем геттер, и, таким образом, проверять единичный тест.
Ответ 8
Юмористический, но мудрый взгляд: Путь Фестиваля
"Напишите тест, который вы можете сегодня"
Тестирование геттеров/сеттеров может быть излишним, если вы опытный тестировщик, и это небольшой проект. Однако, если вы только начинаете изучать юнит-тестирование, или эти методы получения/установки могут содержать логику (например, пример @ArtB setMom()
), тогда было бы неплохо написать тесты.
Ответ 9
Это действительно была недавняя тема между моей командой и я. Мы снимаем для покрытия 80% кода. Моя команда утверждает, что геттеры и сеттеры автоматически реализованы, и компилятор генерирует базовый код за кулисами. В этом случае, если генерируемый код не является навязчивым, на самом деле не имеет смысла тестировать код, создаваемый компилятором для вас. Мы также обсудили асинхронные методы, и в этом случае компилятор генерирует целую кучу кода за кулисами. Это другой случай и то, что мы тестируем. Длинный ответ короткий, поднимите его с вашей командой и решите для себя, если его стоит проверить.
Кроме того, если вы используете отчет о покрытии кода, например, мы можем сделать это, добавьте атрибут [ExcludeFromCodeCoverage]. Наше решение состояло в том, чтобы использовать это для моделей, которые имеют только свойства с использованием геттеров и сеттеров или самим свойством. Таким образом, это не повлияет на общий охват кода% при запуске отчета о покрытии кода, предполагая, что это то, что вы используете для расчета процентного охвата кода. Счастливое тестирование!
Ответ 10
По-моему, охват кода - хороший способ увидеть, пропустили ли вы какую-либо функциональность, которую вы должны покрыть.
Когда вы проверяете покрытие вручную с помощью красивой раскраски, можно утверждать, что простые геттеры и сеттеры не нуждаются в проверке (хотя я всегда это делаю).
Когда вы проверяете процент покрытия кода на своем проекте, процент опроса, равный 80%, не имеет смысла. Вы можете проверить все логические части и забыть некоторые важные части. В этом случае только 100% означает, что вы протестировали весь ваш жизненно важный код (и весь нелогичный код). Как только это 99,9%, вы знаете, что что-то забыли.
Кстати: охват кода - это окончательная проверка, чтобы проверить, полностью ли вы тестировали класс. Но 100% охват кода не обязательно означает, что вы действительно протестировали все функциональные возможности этого класса. Поэтому модульные тесты всегда должны выполняться по логике класса. В конце вы запустите покрытие, чтобы узнать, не забыли ли вы что-нибудь. Когда вы это сделаете правильно, вы нажмете 100% в первый раз.
Еще одна вещь: недавно работая в большом банке в Нидерландах, я заметил, что Sonar указала 100% -ный охват кода. Однако я знал, что чего-то не хватает. Проверяя процент заполнения кода на файл, он указал файл с меньшим процентом. Весь процент кодовой базы был таким большим, что в одном файле процент не отображался как 99,9%. Таким образом, вы можете посмотреть на это...
Ответ 11
Я сделал немного анализ охвата, достигнутого в самом коде JUnit.
Одна категория непокрытого кода "слишком проста для тестирования". Это включает в себя простые геттеры и сеттеры, которые разработчики JUnit не тестируют.
С другой стороны, JUnit не имеет какого-либо (не устаревшего) метода длиной более 3 строк, которые не покрываются никаким тестом.
Ответ 12
Я бы сказал: ДА Ошибки в методах получения/установки могут незаметно проникнуть и вызвать некоторые уродливые ошибки.
Я написал библиотеку, чтобы упростить этот и некоторые другие тесты. Единственное, что вы должны написать в своих тестах JUnit, это:
assertTrue(executor.execute(Example.class, Arrays.asList( new DefensiveCopyingCheck(),
new EmptyCollectionCheck(), new GetterIsSetterCheck(),
new HashcodeAndEqualsCheck(), new PublicVariableCheck())));
Ответ 13
Да, особенно если элемент, который нужно получить, является объектом класса, подклассом которого является абстрактный класс. Ваша IDE может или не может предупредить вас, что определенное свойство не было инициализировано.
А затем какой-то явно не связанный тест завершается с NullPointerException
, и вам требуется некоторое время, чтобы понять, что свойство gettable на самом деле не для того, чтобы получить его в первую очередь.
Хотя это все равно было бы не так плохо, как обнаружение проблемы на производстве.
Было бы неплохо убедиться, что все ваши абстрактные классы имеют конструкторы. Если нет, тест получателя может предупредить вас о проблеме там.
Что касается получателей и установщиков примитивов, может возникнуть вопрос: я тестирую свою программу или я тестирую JVM или CLR? Вообще говоря, JVM не нуждается в проверке.