2011-01-07 2 views
9

Наша команда находится в процессе ослабления TDD и борется с лучшими практиками для модульных тестов. В нашем тестируемом коде используется инъекция зависимостей. Наши тесты обычно следуют за компоновкой Arrange-Act-Assert, где мы издеваемся над зависимостями в разделе Arrange с Moq.Многоразовые издевки против насмешек в каждом тесте

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

Рассмотрим упрощенный пример:

  • XRepository.Save имеет это подпись и поведение/контракт изменен.
  • XController.Save использует XRepository.Save, поэтому он реорганизован для использования нового интерфейса. Но внешне это публичный контракт не изменился.

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

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

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

Так мои вопросы:

  1. Любое предпочтение для использования на лета издевается в каждом тесте против создания многоразового ручной работы макета для каждого интерфейса?

  2. Учитывая мою историю, я пропустил какой-то принцип или попал в общую ловушку?

Спасибо!

+2

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

+1

Что-то, чтобы рассмотреть: http://en.wikipedia.org/wiki/Interface_segregation_principle – TrueWill

ответ

9

Ваш недостающий принцип, но это распространенная проблема. Я думаю, что каждая команда решает его (или нет) по-своему.

Побочные эффекты

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

  • Это был/не был вызван
  • число раз его называли
  • Какие аргументы были передано ему
  • Порядок звонков.

Обеспечение этого в тесте обычно означает нарушение инкапсуляции (я взаимодействую и знаю с реализацией). Каждый раз, когда вы это делаете, вы всегда неявно связываете тест с реализацией. Это приведет к необходимости обновления теста при обновлении частей реализации, которые вы подвергаете/тестируете.

многоразовый Mocks

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

Прием TDD

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

Я использовал TDD с модульными испытаниями. Я столкнулся с проблемами, которые, по моему мнению, нам не пришлось иметь дело. В частности, вокруг рефакторов я заметил, что нам обычно приходилось обновлять многие тесты. Эти рефактории не были частью единицы кода, а скорее реструктуризацией основных компонентов. Я знаю, что многие люди скажут, что проблема заключалась в частых крупных изменениях, а не в модульном тестировании. Вероятно, есть некоторые истины в отношении больших изменений, частично являющихся результатом нашего планирования/архитектуры. Тем не менее, это было также связано с бизнес-решениями, которые вызвали изменения в направлениях. Эти и другие законные причины привели к необходимости внесения больших изменений в код. Конечным результатом были большие рефакторины, становящиеся более медленными и болезненными в результате всех обновлений теста.

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

Тогда у компании были увольнения. Внезапно у нас не было такого же количества ресурсов, чтобы бросать на программирование и обслуживание. Мы были вынуждены получить наибольшую отдачу за все, что мы делали, включая тестирование. Начнем с того, что мы добавили то, что мы назвали частичными испытаниями стека, чтобы покрыть общие проблемы интеграции, которые у нас были. Они оказались настолько эффективными, что начали делать менее классические модульные тесты. Мы также избавились от ручных приемочных испытаний (Selenium). Мы медленно продвигались туда, где тесты начали тестироваться, пока мы практически не выполнили приемочные испытания, но без браузера. Мы будем моделировать метод GET, POST или PUT конкретному контроллеру и проверять критерии приемки.

  • База данных была обновлена ​​правильно
  • правильный код статуса HTTP был возвращен
  • страница была возвращена, что:
    • был Valid HTML 4.01 Strict
    • содержал информацию, которую мы хотели отправить назад к пользователю

Мы закончили с меньшим количеством ошибок. В частности, почти все ошибки интеграции и ошибки из-за больших рефакторов исчезли почти полностью.

Были компромиссы. Просто выяснилось, что плюсы намного перевешивают минусы из-за ситуации. Минусы:

  • Тест обычно был более сложным, и почти каждый испытывает некоторые побочные эффекты.
  • Мы можем сказать, что когда что-то ломается, но это не так целенаправлено, как модульные тесты, поэтому нам нужно сделать больше отладки, чтобы отслеживать, где проблема.
+0

Спасибо. Думаю, мы идем в том же направлении. В настоящее время у нас есть интеграционные тесты, которые идут от объектов уровня домена вниз - они не доходят до контрольных контроллеров напрямую. Контроллеры все еще тестируются, издеваясь над их зависимостями. Спасибо за комментарии. –

1

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

  1. Есть тесты, в которых осуществляется публичный интерфейс, это очень важно, если мы хотим с уверенностью реорганизовать, они доказывают, что мы соблюдаем наш контракт для наших клиентов. Эти тесты лучше всего обслуживаются манекеном многоразового использования ручной работы, который имеет дело с небольшим подмножеством тестовых данных.
  2. Имеются тесты «покрытия». Они, как правило, доказывают, что наша реализация ведет себя правильно, когда зависимости неверно. Мне кажется, что они нужны на лету, чтобы спровоцировать конкретные пути реализации.
+0

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

Смежные вопросы