Мы написали около 3000 тестов - данные жестко запрограммированы, очень мало повторного использования кода. Эта методология начала кусать нас в задницу. По мере изменения системы мы тратим больше времени на исправление неработающих тестов. У нас есть модульные, интеграционные и функциональные тесты.
То, что я ищу, - это окончательный способ написания управляемых и поддерживаемых тестов.
Каркасы
.net
unit-testing
Чак Конвей
источник
источник
Ответы:
Не думайте о них как о «сломанных модульных тестах», потому что это не так.
Это спецификации, которые ваша программа больше не поддерживает.
Не думайте, что это «исправление тестов», а «определение новых требований».
Тесты должны сначала указать ваше приложение, а не наоборот.
Вы не можете сказать, что у вас есть работающая реализация, пока вы не знаете, что она работает. Вы не можете сказать, что это работает, пока вы не протестируете это.
Несколько других заметок, которые могут вам помочь:
источник
Don't think of it as "fixing the tests", but as "defining new requirements".
То, что вы описываете, может быть не таким уж плохим, но указателем на более глубокие проблемы, обнаруженные вашими тестами.
Если бы вы могли изменить свой код, и ваши тесты не сломались бы, это было бы подозрительно для меня. Разница между допустимым изменением и ошибкой заключается только в том, что она запрашивается, а то, что запрашивается, (согласно предположению TDD) определяется вашими тестами.
Твердо закодированные данные в тестах - это хорошая вещь. Тесты работают как фальсификации, а не как доказательства. Если вычислений слишком много, ваши тесты могут быть тавтологическими. Например:
Чем выше абстракция, тем ближе вы подходите к алгоритму и тем самым ближе к сравнению реальной реализации с самим собой.
Лучшее повторное использование кода в тестах - это imho 'Checks', как и в jUnits
assertThat
, потому что они делают тесты простыми. Кроме того, если тесты могут быть подвергнуты рефакторингу для совместного использования кода, возможен и реальный тестируемый код , что сводит тесты к тестированию базы с рефакторингом.источник
У меня тоже была эта проблема. Мой улучшенный подход был следующим:
Не пишите модульные тесты, если они не являются единственным хорошим способом проверить что-либо.
Я полностью готов признать, что модульные тесты имеют самую низкую стоимость диагностики и времени для исправления. Это делает их ценным инструментом. Проблема состоит в том, что при очевидном пробеге может меняться, что модульные тесты часто слишком мелки, чтобы оправдать затраты на поддержание массы кода. Я написал пример внизу, посмотрите.
Используйте утверждения везде, где они эквивалентны модульному тесту для этого компонента. Утверждения обладают хорошим свойством, что они всегда проверяются на протяжении любой отладочной сборки. Таким образом, вместо того чтобы тестировать ограничения класса «Сотрудник» в отдельном блоке тестов, вы эффективно тестируете класс «Сотрудник» в каждом тестовом примере в системе. Утверждения также обладают приятным свойством, заключающимся в том, что они не увеличивают массу кода в большей степени, чем модульные тесты (которые в конечном итоге требуют scaffolding / mocking / что угодно).
Прежде чем кто-то убьет меня: производственные сборки не должны рушиться на утверждениях. Вместо этого они должны регистрироваться на уровне «Ошибка».
В качестве предостережения для тех, кто еще не задумывался об этом, не заявляйте ничего о пользовательском или сетевом вводе. Это огромная ошибка ™.
В моих последних кодах я удалял модульные тесты везде, где вижу очевидную возможность для утверждений. Это значительно снизило стоимость обслуживания в целом и сделало меня намного счастливее.
Предпочитайте системные / интеграционные тесты, внедряя их для всех ваших основных потоков и пользовательского опыта. Угловые шкафы, вероятно, не должны быть здесь. Системный тест проверяет поведение на стороне пользователя путем запуска всех компонентов. Из-за этого системный тест обязательно медленнее, поэтому напишите те, которые имеют значение (не больше, не меньше), и вы поймете самые важные проблемы. Системные тесты имеют очень низкие эксплуатационные расходы.
Важно помнить, что, поскольку вы используете утверждения, каждый системный тест будет запускать пару сотен «модульных тестов» одновременно. Вы также уверены, что самые важные из них запускаются несколько раз.
Напишите сильные API, которые можно функционально протестировать. Функциональные тесты неудобны и (давайте посмотрим правде в глаза) бессмысленно, если ваш API слишком усложняет проверку работоспособных компонентов самостоятельно. Хороший дизайн API а) упрощает этапы тестирования и б) порождает четкие и ценные утверждения.
Функциональное тестирование - самая трудная вещь, чтобы получить право, особенно когда у вас есть компоненты, взаимодействующие один-ко-многим или (еще хуже, о боже) многие-ко-многим через технологические барьеры. Чем больше входов и выходов подключено к одному компоненту, тем сложнее функциональное тестирование, потому что вы должны изолировать один из них, чтобы действительно проверить его функциональность.
По вопросу «не пишите юнит-тесты» я приведу пример:
Автор этого теста добавил семь строк, которые никак не влияют на проверку конечного продукта. Пользователь никогда не должен видеть, что это происходит, потому что a) никто никогда не должен передавать NULL там (поэтому напишите утверждение), или b) случай NULL должен вызывать другое поведение. Если случай (b), напишите тест, который фактически проверяет это поведение.
Моя философия заключается в том, что мы не должны проверять артефакты реализации. Мы должны проверять только то, что можно считать фактическим результатом. В противном случае нет способа избежать написания двойной базовой массы кода между модульными тестами (которые вызывают конкретную реализацию) и самой реализацией.
Здесь важно отметить, что есть хорошие кандидаты на юнит-тесты. Фактически, есть даже несколько ситуаций, когда модульное тестирование является единственным адекватным средством проверки чего-либо и в котором очень важно писать и поддерживать эти тесты. В первую очередь, этот список включает в себя нетривиальные алгоритмы, открытые контейнеры данных в API и высокооптимизированный код, который выглядит «сложным» (иначе «следующий парень, вероятно, облажается»).
Тогда мой конкретный совет: начинайте разумно удалять модульные тесты, когда они ломаются, задавая себе вопрос: «Это вывод или я трачу код?» Возможно, вам удастся сократить количество вещей, которые тратят ваше время.
источник
Мне кажется, что ваше модульное тестирование работает как шарм. Это хорошая вещь , что он настолько хрупок изменениям, так как это своего рода весь смысл. Небольшие изменения в тестах на разрыв кода, так что вы можете исключить возможность ошибки во всей вашей программе.
Однако имейте в виду, что вам действительно нужно тестировать только те условия, которые могут привести к сбою вашего метода или дать неожиданные результаты. Это сделало бы ваше тестирование юнитов более склонным к «поломке», если есть подлинная проблема, а не тривиальные вещи.
Хотя мне кажется, что вы серьезно пересматриваете программу. В таких случаях делайте все, что вам нужно, и удалите старые тесты, а затем замените их новыми. Исправлять юнит-тесты стоит, только если вы не исправляете их из-за радикальных изменений в вашей программе. В противном случае вы можете обнаружить, что слишком много времени уделяете переписыванию тестов, чтобы их можно было применить в только что написанном разделе программного кода.
источник
Я уверен, что другие получат гораздо больше информации, но по моему опыту, это несколько важных вещей, которые помогут вам:
источник
Обрабатывайте тесты так, как вы делаете это с исходным кодом.
Контроль версий, выпуски контрольных точек, отслеживание проблем, «владение функциями», планирование и оценка усилий и т. Д. И так далее. Я думаю, что это наиболее эффективный способ решения проблем, которые вы описываете.
источник
Вам определенно стоит взглянуть на тестовые шаблоны Gerard Meszaros XUnit . У этого есть большой раздел со многими рецептами, чтобы повторно использовать Ваш тестовый код и избежать дублирования.
Если ваши тесты хрупкие, возможно, вы недостаточно прибегаете к двойным тестам. Особенно, если вы воссоздаете целые графы объектов в начале каждого модульного теста, разделы Arrange в ваших тестах могут стать слишком большими, и вы можете часто оказаться в ситуациях, когда вам приходится переписывать разделы Arrange в значительном количестве тестов только потому, что один из ваших наиболее часто используемых классов изменился. В этом вам могут помочь насмешки и заглушки, сократив количество объектов, которые необходимо повторно обработать, чтобы получить соответствующий контекст теста.
Извлечение неважных деталей из ваших тестовых настроек через макеты и заглушки и применение тестовых шаблонов для повторного использования кода должно значительно снизить их хрупкость.
источник