Как разработать код для тестирования - PullRequest
6 голосов
/ 25 ноября 2008

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

Я много читал, что вы должны программировать для интерфейсов , а не классов . Основная проблема, которую я имею, состоит в том, сколько интерфейсов вы должны создать? Если у вас есть один для всего, что вы хотите проверить? или я читаю это неправильно?

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

Ответы [ 7 ]

7 голосов
/ 25 ноября 2008

Я думаю, у вас есть правильная идея, но я думаю, что вы превращаете это в большее дело, чем оно есть. Если вы начнете заниматься TDD, вашей первой реакцией, вероятно, будет «это все?». А потом, надеюсь, вы должны сказать «ага»!

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

Итак, представьте, что вы тестируете калькулятор Add (int 1, int 2), первое, что вы думаете, это «как мне это сломать». Мои идеи о том, где что-то может пойти не так: отрицательные числа, нули и переполнения. Хитрость заключается в том, чтобы представить каждую ошибку, которую может совершить человек, который собирается создать метод Add (), а затем написать тест против него. Итак, я мог бы написать:

Assert.AreEqual(5, calc.Add(2, 3), "Adding positives not as expected");
Assert.AreEqual(-5, calc.Add(-2, -3), "Adding negatives not as expected");
Assert.AreEqual(-2, calc.Add(-3, 2), "Adding one positive and one negative not as expected");

// your framework might provide a cleaner way of doing this:
try {
  int result = calc.Add(Int32.Max, 5);
  Assert.Fail("Expected overflow error. Received: " + result);
} catch(Exception e) {
  // This should be a more specific error that I'm not looking up
}

Итак, как вы можете видеть, я попытался выяснить, как метод Add () может не работать, а затем протестировать его. Я также искал интересные угловые случаи и четко определил ожидаемое поведение. И теперь я могу свободно кодировать метод Add ().

Теперь, хотя это не так уж и здорово, вы знаете, что ваш метод Add () будет непоколебимым, когда вы начнете создавать сложные математические функции, которые объединяют ваш метод Sin () с вашим методом Sqrt () вместе с вашим Add () метод.

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

6 голосов
/ 25 ноября 2008

Прежде всего .. TDD - это не магия / серебряная пуля. Потратьте некоторое время с этим .. это будет стоить вашего времени. Не пытайтесь забрать TDD «на лету» или вне постов. Подберите хорошую книгу (Кент Бек / Дэйв Астелс) и продолжайте свой путь вверх (очень печально видеть людей, бегающих вокруг, когда все, что им нужно, это сделать шаг назад и прочитать.)

Дизайн для тестирования:

  • Сначала напишите кодовый тест .. ваш тестируемый проект должен появиться .. один проходной тест за раз. Вы получаете это как побочный продукт .. больше как удовольствие для того, чтобы сделать это правильно.
  • Желаемое за действительное: представьте, что то, что вы хотите построить, уже существует. Как бы вы поговорили с этим? Это простое моделирование должно передать вам определение внешнего интерфейса «вещь»
  • Сохраняйте это простым / YAGNI / Держите молот: потратьте некоторое время, чтобы осмотреть практики (паттерны, DI, Mocks, вы найдете много и т. Д.), Чтобы выяснить, почему они существуют в первую очередь. Оцените, если они вам нужны, и только потом приступайте к их использованию. Еще одна хорошая вещь для поиска - «где их не использовать?». Если это звучит как плохая идея .. это, вероятно, так. (например, нужен ли мне интерфейс для PR для каждого класса, который я пишу ... вероятно, нет. Нужен ли Spring.net для каждого класса, который я создаю?) Задайте следующие вопросы на каждой контрольной точке (определение простого дизайна К. Бека) )

    • Код и тесты сообщают все, что мне нужно для общения?
    • Есть ли дублирование? (Устранить, если есть)
    • Содержит ли он наименьшее количество возможных классов? ... наименьшее количество возможных методов? Есть ли что-нибудь, что я мог бы отнять у этого дизайна и при этом сохранить поведение.
  • Перефразировать беспощадно: слушайте Мартина Фаулера и держите книгу под рукой

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

3 голосов
/ 27 ноября 2008

Сначала прочитайте Руководство Google по написанию тестируемого кода , которое только что было выпущено 24 ноября.

Это отличный сборник лучших примеров тестируемости Мишко Хевери, тестирующего евангелиста в Google.

3 голосов
/ 25 ноября 2008

Мне нравится использовать эмпирическое правило Кента Бека.

Напишите тест, который описывает ваш идеальный объектный API. Другими словами, напишите код, который вы хотите, чтобы кто-то другой мог написать, чтобы что-то произошло. Возможно, вам не удастся на самом деле реализовать его таким образом, но вы можете начать с ЛУЧШЕГО интерфейса с самого начала, а не с чего-то другого.

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

Надеюсь, это поможет.

1 голос
/ 25 ноября 2008

Если вы примените TDD, вы получите дизайн, который можно тестировать - и тестировать - по мере того, как вы будете расширять свой пакет модульных тестов вместе с вашим кодом. Дизайн появляется , управляемый тестом.

Найдите приблизительный дизайн (*) для решения проблемы, затем начните с класса, который не зависит от другого класса, реализуйте его, сначала проведите тестирование. Затем вы можете реализовать класс, который тоже не имеет зависимости, или класс, который зависит от класса, который вы уже реализовали.

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

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

(*) Вам не нужно разрабатывать все, так как тест-драйв вашего кода может привести вас в другом направлении: см. Статью игра в боулинг , в которой демонстрируются TDD и Эмерджентный дизайн в несколько страниц.

1 голос
/ 25 ноября 2008

Вы движетесь в правильном направлении, но не сходите с ума. Вам не нужен интерфейс для каждого объекта, который вы хотите протестировать, например.

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

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

0 голосов
/ 25 ноября 2008

Вы правы в части Насмешки, если действительно делаете то, что сказали.

Что касается интерфейсов, мой личный метод разработки, учитывающий этот момент, заключается в том, что я сначала пишу, как должен выглядеть основной путь моего приложения (используя мой идеальный макет API, который ничего не делает). Затем, просмотрев мою реализацию Mock и несколько раз доработав ее, я приступил к разработке самого класса. На этом этапе «знаю», как я могу протестировать каждый метод и написать тест для него, и если по какой-то причине я сталкиваюсь с методом, который не могу легко понять, как тестировать, это означает, что метод не очень хороший, скорее всего, у него слишком много обязанностей и слишком много промежуточных состояний, которые я не могу протестировать, поэтому я разбил метод (интерфейс побеждает дизайн класса). И продолжайте просачиваться, пока у меня не будет работоспособного приложения.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...