TDD ... как? - PullRequest
       37

TDD ... как?

9 голосов
/ 10 апреля 2009

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

Я создаю функцию, которая будет читать двоичные данные из сокета и анализировать их данные в объект класса.

Насколько я вижу, есть 3 части:

1) Логика для разбора данных 2) класс сокетов 3) объект класса

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

Ответы [ 6 ]

6 голосов
/ 11 апреля 2009

Проблема в TDD - «дизайн для тестирования»

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

Чтобы попасть туда, вы должны иметь приблизительное представление о том, какие у вас тестируемые единицы измерения.

  1. Какой-то класс, который построен функцией.

  2. Некоторая функция, которая читает из сокета и выдает класс.

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

В-третьих, вы начинаете писать свои тесты - зная, что они скомпилируются, но не получатся.

На полпути вы можете начать ломать голову о своей функции. Как вы настраиваете сокет для своей функции? Это боль в шее.

Тем не менее, интерфейс, который вы описали выше, не является законом, это просто хорошая идея. Что если ваша функция взяла массив байтов и создала объект класса? Это намного, намного проще проверить.

Итак, вернитесь к шагам, измените интерфейс, напишите нерабочий класс и функцию, теперь напишите тесты.

Теперь вы можете заполнять класс и функцию, пока все ваши тесты не пройдут.

Когда вы закончите этот тест, все, что вам нужно сделать, это подключить реальный сокет. Вы доверяете библиотекам сокетов? (Подсказка: вы должны) Не так много, чтобы проверить здесь. Если вы не доверяете библиотекам сокетов, теперь вы должны предоставить источник данных, которые вы можете запускать контролируемым образом. Это большая боль.

5 голосов
/ 10 апреля 2009

Ваш раскол звучит разумно. Я бы посчитал две зависимости входными и выходными. Можете ли вы сделать их менее зависимыми от конкретного производственного кода? Например, вы можете заставить его читать из общего потока данных вместо сокета? Это упростит передачу тестовых данных.

Создание возвращаемого значения может быть труднее смоделировать, и в любом случае это может не быть проблемой - является ли логика, используемая для фактического заполнения результирующего объекта, достаточно простой (после анализа)? Например, это просто установка тривиальных свойств? Если это так, я не стал бы пытаться представить там фабрику и т. Д. - просто введите некоторые данные испытаний и проверьте результаты.

4 голосов
/ 11 апреля 2009

Во-первых, начните думать «тест», во множественном числе, а не «тест», в единственном числе. Вы должны ожидать, чтобы написать более одного.

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

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

Если у вас все еще есть ментальный блок, опустите его еще на одну отметку. Предположим, что ваш байтовый массив будет содержать только значения по умолчанию в любом случае. Таким образом, вам даже не нужно беспокоиться о разборе, просто о возможности вернуть экземпляр вашего объекта, для которого все значения установлены по умолчанию. Вы можете написать тест для этого?

Я представляю что-то вроде:

public void testFooReaderCanParseDefaultFoo() {
  FooReader fr = new FooReader();
  Foo myFoo = fr.buildFoo();
  assertEquals(0, myFoo.bar());
}

Это дно, верно? Вы только тестируете конструктор Foo. Но вы можете перейти на следующий уровень:

public void testFooReaderGivenBytesBuildsFoo() {
  FooReader fr = new FooReader();
  byte[] fooData = {1};
  fr.consumeBytes(fooData);
  Foo myFoo = fr.buildFoo();
  assertEquals(1, myFoo.bar());
}

И так далее ...

2 голосов
/ 31 августа 2017

«Лучшая среда тестирования - это само приложение»

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

Прочитав вики на TDD (https://en.wikipedia.org/wiki/Test-driven_development),), я пришел к выводу, что в некоторой степени вещи немного открыты для интерпретации.

Существуют различные персональные стили TDD, в основном из-за того, что принципы TDD открыты для интерпретации.

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

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

  2. Повторение слишком большого количества кода приводит к проблемам с управлением кодом и основам кода слишком большого размера. Однако у него есть преимущество в разделении проблем (возможность настраивать, изменять и исправлять вещи, не затрагивая другие части приложения).

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

Принимая решение о повторном использовании кода, я основываю решение на следующем: .... Изменится ли природа этого кода в контексте во всей кодовой базе приложения? Если ответ «нет», я использую его повторно. Если ответ «да» или я не уверен, я повторяю / реорганизую его. Однако я буду время от времени пересматривать свои кодовые базы и проверять, можно ли объединить любой из моих повторяющихся кодов без ущерба для разделения интересов / абстракции.

Что касается моих базовых навыков программирования, я хотел бы сначала написать условия (если затем переключить регистр и т. Д.); протестируйте их, затем заполните условия кодом и протестируйте снова. Имейте в виду, нет правила, что вы должны сделать это в модульном тесте. Я называю это вещью низкого уровня. Как только мои низкоуровневые вещи будут выполнены, я либо повторно использую код, либо реорганизую его в другую часть приложения, но не после тщательного тестирования. Проблема с повторением / рефакторингом плохо протестированного кода заключается в том, что, если он сломан, его нужно исправить в нескольких местах.

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

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

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

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

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

Как общая стратегия разделения интересов, в большинстве приложений я разделяю интересы на основе форм пользовательского интерфейса. Например, я буду повторно использовать код в форме и повторять / реорганизовывать формы. Это всего лишь универсальное правило. Бывают моменты, когда я должен мыслить нестандартно. Иногда повторяющийся код может быть полезен для повышения эффективности процессора кода.

Как небольшое дополнение к моим привычкам TDD; Я делаю оптимизации и отказоустойчивость в последнюю очередь. Я постараюсь по возможности избегать использования блоков try catch и писать свой код так, чтобы они не требовались. Например, вместо того, чтобы ловить ноль, я буду проверять на ноль, или вместо того, чтобы ловить индекс вне границ, я буду тщательно проверять свой код, чтобы он никогда не происходил. Я считаю, что перехват ошибок слишком рано при разработке приложения приводит к семантическим ошибкам (поведенческим ошибкам, которые не приводят к сбою приложения). Семантические ошибки могут быть очень трудно отследить или даже заметить. Ну, это мои 10 центов. Надеюсь, это поможет.

1 голос
/ 10 апреля 2009

Test Driven Development? Итак, это означает, что вы должны начать с написания теста в первую очередь.

Напишите тест, содержащий код типа «как вы хотите использовать свой класс». Этот класс или метод, который вы собираетесь тестировать с помощью этого теста, еще даже не существует.

Например, вы могли бы сначала написать тест, например:

[Test]
public void CanReadDataFromSocket()
{
     SocketReader r = new SocketReader( ... ); // create a socketreader instance which takes a socket or a mock-socket in its constructor

     byte[] data = r.Read();

     Assert.IsTrue (data.Length > 0);
}

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

0 голосов
/ 24 сентября 2015

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

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

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