С точки зрения ценности я вижу две группы юнит-тестов в моей практике:
- Тесты, которые проверяют некоторую нетривиальную логику. Написание их (до или после реализации) выявляет некоторые проблемы / потенциальные ошибки и помогает быть уверенным в том случае, если логика изменится в будущем.
- Тесты, которые проверяют очень тривиальную логику. Эти тесты больше похожи на код документа (обычно с макетами), чем тестируют его. Рабочий процесс тех тестов заключается не в том, что «какая-то логика изменилась, тест стал красным - слава Богу, я написал этот тест», но «изменился некоторый тривиальный код, тест стал ложноотрицательным - я должен поддерживать (переписывать) тест, не получая никакой прибыли» , Большую часть времени эти тесты не стоит поддерживать (за исключением религиозных соображений). И, согласно моему опыту во многих системах, эти тесты составляют 80% всех тестов.
Я пытаюсь выяснить, что другие ребята думают о разделении модульных тестов по значению и как оно соответствует моему разделению. Но то, что я чаще всего вижу, это либо пропаганда TDD с полной занятостью, либо пропаганда «бесполезны, просто пишут код». Я заинтересован в чем-то посередине. Ваши собственные мысли или ссылки на статьи / документы / книги приветствуются.
unit-testing
tdd
SiberianGuy
источник
источник
Ответы:
Я думаю, что естественно встретить разрыв в модульном тестировании. Есть много разных мнений о том, как сделать это правильно, и, естественно, все другие мнения по своей сути ошибочны . За последнее время на DrDobbs появилось немало статей, в которых рассматривается именно эта проблема, на которую я ссылаюсь в конце своего ответа.
Первая проблема, которую я вижу с тестами, заключается в том, что их легко понять неправильно. В моем классе C ++ в колледже мы проходили модульные тесты как в первом, так и во втором семестре. Мы ничего не знали о программировании вообще ни в одном из семестров - мы пытались изучить основы программирования через C ++. А теперь представьте, что вы говорите студентам: «О, вы написали небольшой ежегодный налоговый калькулятор! Теперь напишите несколько юнит-тестов, чтобы убедиться, что он работает правильно». Результаты должны быть очевидны - все они были ужасны, включая мои попытки.
Как только вы признаете, что не любите писать модульные тесты и хотите стать лучше, вы скоро столкнетесь с модными стилями тестирования или другими методологиями. Под тестовыми методологиями я имею в виду такие практики, как тестирование вначале или то, что делает Эндрю Бинсток из DrDobbs, то есть написание тестов вместе с кодом. У обоих есть свои плюсы и минусы, и я отказываюсь вдаваться в какие-либо субъективные подробности, потому что это вызовет пламенную войну. Если вас не смущает, какая методология программирования лучше, тогда, возможно, стиль тестирования подойдет. Стоит ли использовать TDD, BDD, тестирование на основе свойств? У JUnit есть продвинутые концепции, называемые Теориями, которые стирают грань между TDD и тестированием на основе свойств. Что использовать, когда?
tl; dr Легко ошибиться в тестировании, это невероятно самоуверенно, и я не верю, что какая-либо одна методология тестирования по своей сути лучше, если она используется усердно и профессионально в контексте, в котором они уместны. Кроме того, тестирование На мой взгляд, это расширение утверждений или проверок работоспособности, которые использовались для обеспечения быстрого и безотказного подхода к разработке, который теперь стал намного, намного проще.
Для субъективного мнения я предпочитаю писать «фазы» тестов, из-за отсутствия лучшей фразы. Я пишу модульные тесты, которые тестируют классы изолированно, используя при необходимости макеты. Возможно, они будут выполнены с помощью JUnit или чего-то подобного. Затем я пишу интеграционные или приемочные тесты, которые проводятся отдельно и обычно только несколько раз в день. Это ваш нетривиальный вариант использования. Я обычно использую BDD, так как приятно выражать функции на естественном языке, что JUnit не может легко предоставить.
Наконец, ресурсы. Они будут представлять противоречивые мнения, в основном связанные с модульным тестированием на разных языках и с разными структурами. Они должны представлять разрыв в идеологии и методологии, позволяя вам составить собственное мнение, если я уже не слишком манипулировал вашим мнением :)
[1] Коррупция Agile Эндрю Бинсток
[2] Ответ на ответы предыдущей статьи
[3] Реакция дяди Боба на проворство гибкости
[4] Реакция Роберта Майерса на коррупцию Agile
[5] Зачем беспокоиться о тестировании огурцов?
[6] Ты делаешь это неправильно
[7] Отойдите от инструментов
[8] Комментарий к «Римские цифры Ката с комментарием»
[9] Римские цифры Ката с комментариями
источник
Я считаю, что важно иметь тесты обоих типов и использовать их там, где это необходимо.
Как вы сказали, есть две крайности, и я, честно говоря, не согласен ни с одной из них.
Ключ заключается в том, что модульные тесты должны охватывать бизнес-правила и требования . Если существует требование, чтобы система отслеживала возраст человека, напишите «тривиальные» тесты, чтобы убедиться, что возраст является неотрицательным целым числом. Вы тестируете область данных, необходимых для системы: хотя она и тривиальна, она имеет значение, поскольку она обеспечивает выполнение параметров системы .
Аналогично с более сложными тестами, они должны приносить ценность. Конечно, вы можете написать тест, который проверяет что-то, что не является обязательным, но должно выполняться где-то в башне из слоновой кости, но это время, потраченное на написание тестов, которые подтверждают требования, за которые заказчик платит вам. Например, зачем писать тест, который проверяет ваш код, может иметь дело с потоком ввода, который истекает, когда единственными потоками являются локальные файлы, а не сеть?
Я твердо верю в модульное тестирование и использую TDD везде, где это имеет смысл. Модульные тесты, безусловно, приносят пользу в виде повышенного качества и «быстрого отказа» при изменении кода. Однако следует помнить и старое правило 80/20 . В какой-то момент вы достигнете убывающей отдачи при написании тестов, и вам нужно перейти к более продуктивной работе, даже если есть некоторая измеримая ценность, которую нужно иметь при написании большего количества тестов.
источник
Вот мое мнение: все тесты имеют стоимость:
Мы также стремимся к тому, чтобы все тесты приносили пользу (и, по моему опыту, почти все тесты дают преимущества):
Поэтому довольно легко увидеть, что если вы напишите несколько тестов, они, вероятно, будут иметь какую-то ценность. Ситуация усложняется, когда вы начинаете сравнивать это значение (которое, кстати, вы можете не знать заранее - если вы выбросите свой код, регрессионные тесты теряют свою ценность) со стоимостью.
Теперь ваши время и усилия ограничены. Вы хотели бы сделать те вещи, которые приносят наибольшую пользу при наименьших затратах. И я думаю, что это очень сложно сделать, не в последнюю очередь потому, что для этого могут потребоваться знания, которых у человека нет или они будут дорогостоящими.
И это настоящий разрыв между этими разными подходами. Я считаю, что все они определили стратегии тестирования, которые являются полезными. Тем не менее, каждая стратегия имеет разные затраты и выгоды в целом. Кроме того, затраты и выгоды каждой стратегии, вероятно, будут сильно зависеть от специфики проекта, области и команды. Другими словами, может быть несколько лучших ответов.
В некоторых случаях откачка кода без тестов может обеспечить наилучшие преимущества / затраты. В других случаях может быть лучше тщательный набор тестов. В других случаях улучшение дизайна может быть лучшим решением.
источник
Что это блок испытания, на самом деле? И действительно ли здесь такая большая дихотомия?
Мы работаем в области, где чтение буквально через один бит после конца буфера может привести к полному сбою программы или вызвать ее совершенно неточный результат, или, как свидетельствует недавняя ошибка TLS «HeartBleed», создать предположительно безопасную систему в целом. открыть без предоставления каких-либо прямых доказательств недостатка.
Невозможно устранить всю сложность этих систем. Но наша работа, насколько это возможно, сводить к минимуму и управлять этой сложностью.
Является ли модульный тест тестом, который подтверждает, например, что резервирование успешно проведено в трех разных системах, создается запись в журнале и отправляется подтверждение по электронной почте?
Я собираюсь сказать нет . Это интеграция тест. И те, кто определенно имеют свое место, но это также другая тема.
Интеграционный тест работает для подтверждения общей функции всей «функции». Но код этой функции должен быть разбит на простые тестируемые строительные блоки, называемые «блоками».
Поэтому юнит-тест должен иметь очень ограниченную область применения.
Что означает , что код протестирован с помощью модульного тестирования должен иметь очень ограниченную сферу применения.
Что также подразумевает, что одним из столпов хорошего дизайна является разбиение вашей сложной задачи на более мелкие одноцелевые куски (насколько это возможно), которые можно тестировать в относительной изоляции друг от друга.
В итоге получается система, созданная из надежных базовых компонентов, и вы знаете, что какой-либо из этих основополагающих блоков кода ломается, потому что вы написали простые, небольшие тесты с ограниченным объемом, чтобы точно сказать вам это.
Во многих случаях вы также должны иметь несколько тестов на единицу. Сами тесты должны быть простыми, тестировать одно и только одно поведение в максимально возможной степени.
Я думаю, что понятие «модульного теста», тестирующего нетривиальную, сложную и сложную логику, немного оксюморон.
Таким образом, если произошел такой умышленный отказ от проектирования, то как в мире может модульный тест внезапно начать выдавать ложные срабатывания, если основная функция тестируемого кода не изменилась? И если это произошло, то вам лучше поверить, что в игре есть неочевидные волновые эффекты. Ваш неработающий тест, который, похоже, дает ложное срабатывание, фактически предупреждает вас о том, что какое-то изменение нарушило более широкий круг зависимостей в базе кода, и его необходимо изучить и исправить.
Некоторые из этих модулей (многие из них) могут нуждаться в тестировании с использованием фиктивных объектов, но это не означает, что вам нужно писать более сложные или сложные тесты.
Возвращаясь к моему надуманному примеру системы бронирования, вы действительно не можете посылать запросы от к живой базе данных резервирования или третьей стороны обслуживанию (или даже «Dev» экземпляру этого) каждый раз , когда вы блок тестового кода.
Таким образом, вы используете макеты, которые представляют один и тот же интерфейсный контракт. Затем тесты могут проверить поведение относительно небольшого детерминированного фрагмента кода. Зеленый все вниз по доске затем говорит вам, что блоки , составляющие ваш фундамент , не сломаны.
Но сама логика отдельных юнит-тестов остается максимально простой.
источник
Это, конечно, мое мнение, но, потратив последние несколько месяцев на изучение функционального программирования на fsharp (из опыта C #), я понял несколько вещей.
Как указывалось в ОП, обычно мы видим два типа «модульных тестов». Тесты, которые охватывают входы и выходы метода, которые, как правило, являются наиболее ценными, но их трудно выполнить для 80% системы, в которой меньше говорится об «алгоритмах» и больше о «абстракциях».
Другой тип, это тестирование интерактивности абстракции, обычно включает в себя насмешку. По моему мнению, эти тесты в основном необходимы из-за дизайна вашего приложения. Откажитесь от них, и вы рискуете получить странные баги и спагетти-код, потому что люди не думают о своем дизайне должным образом, если они не вынуждены сначала делать тесты (и даже тогда, как правило, портят их). Проблема не столько в методологии тестирования, сколько в структуре системы. Большинство систем, построенных на императивных или ОО-языках, имеют наследственную зависимость от «побочных эффектов», то есть «Делай это, но не говори мне ничего». Когда вы полагаетесь на побочный эффект, вам необходимо протестировать его, потому что бизнес-требование или операция обычно является его частью.
Когда вы разрабатываете свою систему более функциональным образом, где вы избегаете создания зависимостей от побочных эффектов и избегаете изменений состояния / отслеживания с помощью неизменяемости, это позволяет вам уделять больше внимания тестам «входы и выходы», которые явно тестируют больше действий и меньше как туда добраться. Вы будете удивлены тем, что такие вещи, как неизменяемость, могут дать вам с точки зрения гораздо более простых решений тех же проблем, а когда вы больше не зависите от «побочных эффектов», вы можете выполнять такие вещи, как распараллеливание и асинхронное программирование практически без дополнительных затрат.
С тех пор, как я начал писать код на Fsharp, мне ни для чего не понадобился фреймворк, и я даже полностью избавился от зависимости от контейнера IOC. Мои тесты основаны на бизнес-потребностях и ценности, а не на сложных уровнях абстракции, обычно необходимых для достижения композиции в императивном программировании.
источник