TDD... как?

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

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

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

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

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

Ответ 1

Проблема в TDD - это "дизайн для проверки"

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

Чтобы попасть туда, вы должны иметь общее представление о том, что ваши тестируемые единицы.

  • Некоторые классы, построенные функцией.

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

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

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

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

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

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

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

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

Ответ 2

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

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

Ответ 3

Сначала начните думать "testS", множественное число, а не "тест", единственное. Вы должны ожидать, чтобы написать несколько экземпляров.

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

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

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

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

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());
}

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

Ответ 4

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

Ответ 5

Испытательная разработка? Таким образом, это означает, что вам следует начать с написания теста в первую очередь.

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

Например, вы можете сначала написать тест следующим образом:

[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", который берет прочитанные вами данные и выводит экземпляр вашего класса данных. и т.д...

Ответ 6

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

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

Ответ 7

Когда я начинал с TDD, я прочитал эти 3 правила дяди Боба, которые действительно помогли мне:

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

В более короткой версии это будет:

  1. Запишите только достаточное количество юнит-тестов.
  2. Напишите только достаточный производственный код, чтобы пройти неудачный юнит-тест.

как видите, это очень просто.