философия модульного тестирования - PullRequest
3 голосов
/ 24 мая 2011

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

  public void HandleNewData(Data data)
  {
     var existingDataStore = dataProvider.Find(data.ID);
     if (data == null)
        return;

     UpdateDataStore(existingDataStore, data, CurrentDateTime);

     NotifyReceivedData(data);

     if (!dataValidator.Validate(data))
        return;

     //... more operations similar to above
  }

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

Так в чем же реальная польза написания такого теста? Или это действительно не стоит беспокоиться?

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

Ответы [ 3 ]

9 голосов
/ 24 мая 2011

TDD не означает написание модульных тестов для уже существующего кода (хотя иногда это может быть необходимо при улучшении устаревшего кода).

Вы, наверное, слышали термин "красный, зеленый, рефакторинг". Это подход, который мы используем при выполнении TDD. Вот три закона развития, основанного на тестировании, которые идут немного дальше ...

  1. Вы не можете писать производственный код пока вы не написали провал модульный тест.
  2. Вы не можете писать больше модульных тестов, чем достаточно для отказа, и не компилируется не удается.
  3. Вы не можете писать больше производственного кода, чем достаточно для передачи в настоящее время не проходит тест.

Преимущества такого подхода состоят в том, что вы получаете очень близкое к 100% покрытие модульным тестом и знаете, что ваш код работает точно так, как указано.

Это уменьшит проблем с обслуживанием, потому что как только кто-то внесет изменения в ваш код и запустит тесты, он узнает, что он что-то сломал.

В этом случае я бы постепенно добавил модульные тесты для методов, вызываемых с HandleNewData(), перед добавлением любых для HandleNewData().

Добавление модульных тестов в унаследованный код сложное, но выполнимое и очень стоящее усилие. Если вы еще этого не сделали, я настоятельно рекомендую прочитать Эффективная работа с устаревшим кодом от Michael Feathers . Я нашел это неоценимым при добавлении модульных тестов в 25-летнюю базу кода.

3 голосов
/ 24 мая 2011

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

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

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

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

Конечно, у вас все еще есть задолженность по сравнению с оригинальным устаревшим кодом, но вы можете решить эту проблему как отдельную проблему.

1 голос
/ 24 мая 2011

Поскольку вы уже пояснили, что это " Legacy Code (TM) ", я не стану спешить с разработкой этого метода. Само имя расплывчато, и это отражено в содержании метода. Я немного посмотрю на улучшение дизайна - кажется, он делает много.

Но чтобы сделать это, я должен был убедиться, что я не ухудшу его под предлогом "сделать его лучше". Как мне это доказать? Тесты!

Итак, я бы начал с того, что поставил «порочные» тесты на «порции» функциональности объектов верхнего уровня. Поэтому я бы включил тесты, которые проверяют поведение «HandleNewData», насколько мне известно сегодня (это может включать некоторые раскопки кода)

  • ищет хранилище данных для идентификатора
  • обновляет хранилище данных новыми данными и пересматривает временную метку
  • уведомляет заинтересованных слушателей
  • проверяет данные (это должен быть шаг 2) и т.д ..

После того, как некоторые автоматические «порочные» тесты закрепят существующее поведение, я теперь могу уверенно вносить изменения / улучшения. Также может быть так, что после рефакторинга дизайна содержащий тип для HandleNewData больше не нужен. В каких случаях вы можете сдуть эти тесты - однако значение этих тестов между EXISTING-IMPROVED нельзя игнорировать.

...