Поэтому сегодня я поговорил с моим товарищем по команде о модульном тестировании. Все началось, когда он спросил меня: «Привет, где тесты для этого класса, я вижу только один?». Весь класс был менеджером (или службой, если вы предпочитаете называть это так), и почти все методы просто делегировали вещи в DAO, так что это было похоже на:
SomeClass getSomething(parameters) {
return myDao.findSomethingBySomething(parameters);
}
Это своего рода шаблон без логики (или, по крайней мере, я не считаю такое простое делегирование логикой), но в большинстве случаев полезный шаблон (разделение слоев и т. Д.). И у нас было довольно продолжительное обсуждение того, должен ли я проводить юнит-тестирование (думаю, стоит упомянуть, что я полностью проверил юнит-тестирование DAO). Его основные аргументы заключаются в том, что это не TDD (очевидно), и что кто-то может захотеть посмотреть тест, чтобы проверить, что делает этот метод (я не знаю, как он может быть более очевидным) или что в будущем кто-то может захотеть изменить реализации и добавить новую (или более похожую на «любую») логику к ней (в этом случае я думаю, что кто-то должен просто проверить эту логику ).
Это заставило меня задуматься. Должны ли мы стремиться к максимальному охвату тестами%? Или это просто искусство ради искусства? Я просто не вижу никакой причины для тестирования таких вещей, как:
- геттеры и сеттеры (если в них нет логики)
- "шаблонный" код
Очевидно, что тестирование такого метода (с использованием имитаций) заняло бы у меня меньше минуты, но я полагаю, что это все еще пустая трата времени и дольше миллисекунды для каждого CI.
Существуют ли какие-либо рациональные / не "легковоспламеняющиеся" причины, по которым нужно тестировать каждую (или столько, сколько он может) строку кода?
источник
Ответы:
Я следую эмпирическому правилу Кента Бека:
Проверьте все, что может сломаться.
Конечно, это в некоторой степени субъективно. Для меня тривиальные геттеры / сеттеры и однострочные, как у вас выше, обычно не стоят того. Но опять же, я трачу большую часть своего времени на написание модульных тестов для унаследованного кода, мечтая только о хорошем новом проекте TDD ... В таких проектах правила разные. При использовании унаследованного кода основная цель - охватить как можно больше усилий с минимальными усилиями, поэтому модульные тесты, как правило, более высокого уровня и более сложные, больше похожи на интеграционные тесты, если кто-то педантичен в отношении терминологии. И когда вы боретесь за то, чтобы увеличить общее покрытие кода с 0%, или просто сумели поднять его более чем на 25%, методы получения и установки модульного тестирования - это наименьшее из ваших беспокойств.
OTOH в новом проекте TDD, может быть более логично писать тесты даже для таких методов. Тем более, что вы уже написали тест до того, как у вас появится возможность задуматься: «Стоит ли эта строка для отдельного теста?». По крайней мере, эти тесты тривиальны для написания и быстрого запуска, так что в любом случае это не имеет большого значения.
источник
Существует несколько видов юнит-тестирования:
Если бы вы сначала написали свой тест, то это имело бы больше смысла - как вы ожидаете вызвать слой доступа к данным. Сначала тест не пройдёт. Затем вы должны написать производственный код для прохождения теста.
В идеале вы должны тестировать логический код, но взаимодействия (объекты, вызывающие другие объекты) одинаково важны. В твоем случае я бы
В настоящее время там нет логики, но так будет не всегда.
Однако, если вы уверены, что в этом методе не будет логики и он, вероятно, останется прежним, я бы посоветовал вызывать уровень доступа к данным напрямую от потребителя. Я сделал бы это, только если остальная часть команды находится на той же странице. Вы не хотите отправлять неверное сообщение команде, говоря: «Эй, ребята, это нормально, игнорировать уровень домена, просто вызовите уровень доступа к данным напрямую».
Я бы также сконцентрировался на тестировании других компонентов, если бы для этого метода был интеграционный тест. Мне еще предстоит увидеть компанию с серьезными интеграционными тестами.
Сказав все это, я бы не стал вслепую проверять все. Я бы установил горячие точки (компоненты с высокой сложностью и высоким риском поломки). Я бы тогда сосредоточился на этих компонентах. Нет смысла иметь кодовую базу, где 90% кодовой базы довольно просты и покрыты модульными тестами, тогда как оставшиеся 10% представляют базовую логику системы и не охватываются модульными тестами из-за их сложности.
Наконец, в чем выгода тестирования этого метода? Каковы последствия, если это не работает? Они катастрофические? Не стремитесь получить высокий охват кода. Покрытие кода должно быть результатом хорошего набора модульных тестов. Например, вы можете написать один тест, который пройдет по дереву и даст вам 100% охват этого метода, или вы можете написать три модульных теста, которые также обеспечат вам 100% покрытие. Разница в том, что при написании трех тестов вы тестируете крайние случаи, а не просто обходите дерево.
источник
Вот хороший способ подумать о качестве вашего программного обеспечения:
Для стандартных и тривиальных функций вы можете полагаться на проверку типов при выполнении своей работы, а в остальном вам нужны тестовые случаи.
источник
На мой взгляд, цикломатическая сложность является параметром. Если метод не является достаточно сложным (например, методы получения и установки). Модульное тестирование не требуется. Уровень цикломатической сложности McCabe должен быть больше 1. Другим словом должно быть не менее 1 оператора блока.
источник
Громкое ДА с TDD (и с несколькими исключениями)
Спорные хорошо, но я бы утверждать, что тот, кто отвечает «нет» на этот вопрос отсутствует фундаментальное понятие TDD.
Для меня ответ - да, если ты следуешь TDD. Если нет, то нет правдоподобного ответа.
DDD в TDD
Часто упоминается, что TDD имеет основные преимущества.
Отдельная ответственность от реализации
Как программисты, ужасно заманчиво думать об атрибутах как о чем-то значимом, а о получателях и установщиках - как о неких накладных расходах.
Но атрибуты - это детали реализации, а сеттеры и геттеры - это контрактный интерфейс, который фактически заставляет программы работать.
Гораздо важнее записать, что объект должен:
а также
затем, как это состояние на самом деле хранится (для которого атрибут является наиболее распространенным, но не единственным способом).
Тест, такой как
важно для документации части TDD.
Тот факт, что возможная реализация является тривиальным (атрибутом) и не приносит никакой пользы для защиты, должен быть неизвестен вам при написании теста.
Отсутствие круговой инженерии ...
Одной из ключевых проблем в мире разработки систем является отсутствие кругового инжиниринга 1 - процесс разработки системы разбивается на отдельные подпроцессы, артефакты которых (документация, код) часто несовместимы.
1 Броди, Майкл Л. «Джон Милопулос: шить семена концептуального моделирования». Концептуальное моделирование: основы и приложения. Springer Berlin Heidelberg, 2009. 1-9.
... и как TDD решает это
Это часть документации TDD, которая обеспечивает согласованность спецификаций системы и ее кода.
Сначала проект, потом реализация
В рамках TDD мы сначала пишем неудачный приемочный тест, а затем пишем код, который позволяет им пройти.
В BDD более высокого уровня мы сначала пишем сценарии, а затем заставляем их проходить.
Почему вы должны исключить сеттеры и геттеры?
Теоретически, в рамках TDD один человек может написать тест, а другой - реализовать код, который делает его успешным.
Так что спросите себя:
Поскольку методы получения и установки публичный интерфейс класса, то ответ, очевидно , да , или не будет никакого способа набора или запроса состояния объекта.
Очевидно, что если вы сначала напишите код, ответ может быть не таким четким.
Исключения
Есть некоторые очевидные исключения из этого правила - функции, которые являются четкими деталями реализации и явно не являются частью дизайна системы.
Например, локальный метод 'B ()':
Или частная функция
square()
здесь:Или любая другая функция, которая не является частью
public
интерфейса, который нуждается в написании при разработке системного компонента.источник
Столкнувшись с философским вопросом, вернитесь к требованиям вождения.
Ваша цель - выпускать достаточно надежное программное обеспечение по конкурентоспособным ценам?
Или это для производства программного обеспечения с максимально возможной надежностью практически независимо от стоимости?
До некоторой степени, две цели - качество и скорость разработки - соотносятся: вы тратите меньше времени на написание тестов, чем на исправление дефектов.
Но за этим пунктом они не делают. Скажем, не так сложно найти, скажем, одну сообщенную ошибку на разработчика в месяц. Сокращение вдвое до одного раза в два месяца приводит к освобождению бюджета, возможно, на день или два, и такое дополнительное тестирование, вероятно, не уменьшит частоту ваших дефектов вдвое. Так что это уже не простой выигрыш / выигрыш; Вы должны обосновать это исходя из стоимости дефекта для клиента.
Эта стоимость будет варьироваться (и, если вы хотите быть злым, то будет зависеть и их способность принудительно возместить эти расходы, будь то через рынок или судебный процесс). Вы не хотите быть злым, поэтому вы полностью рассчитываете эти затраты; иногда некоторые испытания все еще делают мир беднее своим существованием.
Короче говоря, если вы попытаетесь слепо применить те же стандарты к внутреннему веб-сайту, что и программное обеспечение для полетов пассажирских авиалайнеров, вы окажетесь либо вне бизнеса, либо в тюрьме.
источник
Ваш ответ на этот вопрос зависит от вашей философии (вы считаете, что это Чикаго против Лондона? Я уверен, что кто-то найдет это). Жюри до сих пор не решило вопрос о наиболее эффективном по времени подходе (потому что, в конце концов, это самый большой драйвер этого времени, затрачиваемого на исправления).
Некоторые подходы говорят, что тестируют только открытый интерфейс, другие говорят, что проверяют порядок каждого вызова функции в каждой функции. Было много священных войн. Мой совет - попробовать оба подхода. Выберите код и сделайте его как X, а другой как Y. После нескольких месяцев тестирования и интеграции вернитесь и посмотрите, какой из них лучше соответствует вашим потребностям.
источник
Это сложный вопрос.
Строго говоря, я бы сказал, что в этом нет необходимости. Вам лучше писать модульные и системные тесты в стиле BDD, обеспечивающие функционирование бизнес-требований в соответствии с положительными и отрицательными сценариями.
Тем не менее, если ваш метод не охватывается этими тестовыми примерами, то вам нужно задать вопрос, почему он существует в первую очередь и нужен ли он, или если в коде есть скрытые требования, которые не отражены в вашей документации или пользовательских историях, которые должен быть закодирован в тестовом случае в стиле BDD.
Лично я хотел бы, чтобы охват по линиям был на уровне 85-95% и проходил проверку на основной линии, чтобы гарантировать, что существующее покрытие модульных тестов на строку достигает этого уровня для всех файлов кода и чтобы не было обнаружено ни одного файла.
Предполагая, что соблюдаются лучшие практики тестирования, это дает достаточный охват, не заставляя разработчиков терять время, пытаясь выяснить, как получить дополнительное покрытие для трудноиспользуемого кода или тривиального кода просто ради покрытия.
источник
Проблема заключается в самом вопросе: вам не нужно тестировать все «метаданные» или все «классы», необходимые для тестирования всех функций ваших систем.
Его ключевое мышление в терминах особенностей / поведения, а не в терминах методов и классов. Конечно, здесь есть метод для обеспечения поддержки одной или нескольких функций, в конце весь ваш код проверен, по крайней мере, весь код имеет значение в вашей кодовой базе.
В вашем сценарии, вероятно, этот класс «менеджера» является его избыточным или ненужным (как и все классы с именем, которое содержит слово «менеджер»), или, возможно, нет, но кажется деталью реализации, возможно, этот класс не заслуживает единицы тест, потому что этот класс не имеет никакой соответствующей бизнес-логики. Вероятно, вам нужен этот класс для того, чтобы какая-то функция работала, тест для этой функции покрывает этот класс, так что вы можете реорганизовать этот класс и провести тест, который подтвердит, что важная вещь, ваши функции, все еще работает после рефакторинга.
Думайте об особенностях / поведении, а не о классах методов, я не могу повторить это достаточно много раз.
источник
Да, в идеале 100%, но некоторые вещи не тестируются на единицу.
Геттеры / сеттеры глупы - просто не используйте их. Вместо этого поместите переменную-член в публичный раздел.
Получите общий код и протестируйте его. Это должно быть так просто.
Не делая этого, вы можете пропустить некоторые очень очевидные ошибки. Модульные тесты - это как надежная сеть для выявления ошибок определенного рода, и вы должны использовать их как можно чаще.
И последнее: я нахожусь в проекте, где люди не хотели тратить свое время на написание юнит-тестов для какого-то «простого кода», но позже они решили вообще не писать. В конце части кода превратились в большой шарик грязи .
источник