Подтвердить что ты не робот

Как вы/вы "заставили" себя делать TDD, а не TAD?

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

4b9b3361

Ответ 1

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

Конечно, вы тоже должны писать тесты (регрессионные тесты, интеграционные тесты и т.д.). TDD часто создает хороший дизайн, но не обязательно хороший код тестирования.

Ответ 2

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

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

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

Ответ 3

Как только я начал использовать инъекцию зависимостей, мои классы стали меньше и более специализированными, что позволило мне написать простые модульные тесты, чтобы подтвердить, что они работали. Учитывая ограниченное количество тестов, которые я знал, мой класс должен был перейти на работу, цель моих усилий по TDD стала более ясной. Также было легче определить, какие классы требовали интеграционных тестов из-за зависимостей от внешних ресурсов и которые классифицировали требуемые модульные тесты, которые вставляли mock/stub/fake объекты в SUT, чтобы сузить фокус моего теста.

Ответ 4

  • Это помогает, если у вас есть общая тестовая среда.

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

    Чтобы попасть туда, обратите внимание на общие вещи, которые вы делаете в тестах, которые вы пишете. Выделите их в обобщенную библиотеку один за другим.

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

  • Подход к "тестированию как документация". Не добавляйте/не изменяйте какие-либо формулировки в документации, не подкрепленной соответствующими тестами.

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

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

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

  • Сразу же выберите время написания тестов в начале графика разработки проекта

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

    Конечно, "дополнительное" время для TDD заканчивается чистым экономией времени, но оно не всегда реализуется на самом начальном этапе проекта, что отрицательно влияет на практику TDD ( "Где скриншоты прототипа?" Что значит, вы все еще пишете тесты?").

  • Также старайтесь следовать обычной рекомендуемой практике небольших целевых классов и функций. Это - среди всех других преимуществ - позволяет гораздо легче писать unit test. Пара, которая с № 2 (написав unit test как часть документации API, при разработке API), и ваши API-проекты "волшебным образом" улучшаются, так как вы начинаете замечать слабые места сразу из-за написания тестов на их основе. Как отмечали другие люди, использование какого-либо шаблона/рамки для зависимостей Dependency Injection упрощает создание тестов.

Ответ 5

Программирование пар

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

Ответ 6

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

В моей сборке script я использовал инструмент покрытия кода (NCover), чтобы определить процент кода, который был охвачен тестами, и изначально установить порог на 80%. Если бы я прекратил писать свои тесты, то процент покрытия упал бы ниже порога 80%, а моя сборка script приведет к сбою. Затем я немедленно ударил себя по запястью и напишу пропущенные тесты.

Я постепенно увеличил порог покрытия кода и в итоге стал полноценным конвертером TDD.

Ответ 7

Что помогло мне привить привычную дисциплину, прежде чем делать какие-либо изменения, скажите себе: "Какой тест я пишу, чтобы продемонстрировать, что сработало смену?". Хотя не строго TDD (так как фокус довольно мал), это привнесло тестирование на первый план моего мышления всякий раз, когда система была изменена.

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

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

Ответ 8

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

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

Ответ 9

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

Вот репортаж о том, как я стал конвертером BDD/TDD:

Год назад я понятия не имел, как делать TDD (но действительно хотел (как расстраивает)) и никогда не слышал о BDD... теперь я делаю оба навязчиво. Я был в среде разработки .NET, а не в Java, но я даже заменил кнопку "F5-Run" макросом для запуска Cucumber (BDD) или MBUnit (TDD) в зависимости от того, является ли это Feature/Scenario или Specification. Нет отладчика, если это вообще возможно. $1 в банке, если вы используете отладчик (JOKING (вроде)).

Процесс очень впечатляет. Рамка, которую мы дополнительно используем, - это Oracle, о которой я получил благословение, чтобы понять, и поглотить информацию, и те рамки, которые он/мы используем, - это MavenThought.

Все начинается с BDD. Наш BDD - прямое огурец на железной рубине.

Характеристика:

Сценарий:....  Учитывая, что я делаю бла...  Когда я делаю что-то еще...  Тогда происходят чудесные вещи...

Сценарий:...

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

И TDD, который мы использовали, является своего рода BDD в некотором смысле, потому что мы смотрим на поведение, которое требует SUT (System Under Test), и одно задание указывается для каждой спецификации (класс "тестовый" файл).

Пример:

Вот спецификация для одного поведения: при создании системного теста.

Существует еще одна спецификация (файл класса С# When_blah_happens) для другого поведения, когда свойство изменяется, но оно отделено от другого файла.

using MavenThought.Commons.Testing;
using SharpTestsEx;

namespace Price.Displacement.Module.Designer.Tests.Model.Observers
{
    /// <summary>
    /// Specification when diffuser observer is created
    /// </summary>
    [ConstructorSpecification]
    public class When_diffuser_observer_is_created
        : DiffuserObserverSpecification
    {
        /// <summary>
        /// Checks the diffuser injection
        /// </summary>
        [It]
        public void Should_return_the_injected_diffuser()
        {
            Sut.Diffuser.Should().Be.SameInstanceAs(this.ConcreteDiffuser);
        }
    }
}

Это, вероятно, самое простое поведение для SUT, потому что в этом случае, когда он создан, свойство Diffuser должно быть таким же, как и инжектируемый диффузор. Мне пришлось использовать Concrete Diffuser вместо Mock, потому что в этом случае Diffuser является объектом Core/Domain и не имеет уведомления свойства для интерфейса. 95% времени мы ссылаемся на все наши зависимости, такие как Dep(), вместо того, чтобы вводить реальную вещь.

Часто мы имеем более одного [It] Should_do_xyz(), а иногда и честную установку, например, возможно, до 10 строк stubbing. Это просто очень простой пример, в котором нет спецификаций GivenThat() или AndGivenThatAfterCreated().

Для настройки каждой спецификации обычно требуется только переопределить несколько методов спецификации:

GivenThat() == > это происходит до создания SUT.

CreatSut() == > Мы автоматически замаскируем создание сут с StructureMap, и 90% времени никогда не должны переопределять это, но если вы конструктор, впрыскивающий Concrete, вы должны переопределить это.

AndGivenThatAfterCreated() = > это происходит после создания SUT.

WhenIRun() = > если это не значение [ConstructorSpecification], мы используем это для запуска ОДНОЙ строки кода, которая является поведением, которое мы указываем для SUT

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

Все, что я должен сделать, чтобы запустить спецификацию, выделяет это имя, например, "When_diffuser_observer_is_created" и нажимает F5, потому что помните, для меня F5 запускает задачу Rake либо test: feature [tag], если Cucumber, либо test: class [SUT ]. Имеет смысл для меня, потому что каждый раз, когда вы запускаете отладчик, это выброс, никакого кода не создается (о, и это стоит 1 доллар (шутит)).

Это очень, очень чистый способ определить поведение с TDD и иметь действительно, очень простые SUT и простые спецификации. Если вы попытаетесь стать ковбойским кодером и напишите SUT crappy с жесткими зависимостями и т.д., Вы почувствуете боль от попыток сделать TDD и накормить/бросить ИЛИ укусить пулю и сделать это правильно.

И вот настоящий SUT. Мы получили немного фантазии и используем PostSharp для добавления уведомления о свойствах, измененного на Diffuser, поэтому, следовательно, Post.Cast < > . И снова, почему я ввел бетон, а не макет. В любом случае, поскольку вы видите, что недостающее поведение, определенное в другой спецификации, - это когда что-либо изменяется на диффузоре.

using System.ComponentModel;
using MavenThought.Commons.Events;
using PostSharp;
using Price.Displacement.Core.Products;
using Price.Displacement.Domain;

namespace Price.Displacement.Desktop.Module.Designer.Model.Observers
{
    /// <summary>
    /// Implementation of current observer for the selected product
    /// </summary>
    public class DiffuserObserver : AbstractNotifyPropertyChanged, IDiffuserObserver
    {
        /// <summary>
        /// gets the diffuser
        /// </summary>
        public IDiffuser Diffuser { get; private set; }

        /// <summary>
        /// Initialize with a diffuser
        /// </summary>
        /// <param name="diffuser">The diffuser to observe</param>
        public void Initialize(IDiffuser diffuser)
        {
            this.Diffuser = diffuser;
            this.NotifyInterface().PropertyChanged += (x, e) => this.OnPropertyChanged(e.PropertyName);
        }

        /// <summary>
        /// Gets the notify interface to use
        /// </summary>
        /// <returns>The instance of notify property changed interface</returns>
        protected INotifyPropertyChanged NotifyInterface()
        {
            return Post.Cast<Diffuser, INotifyPropertyChanged>((Diffuser)Diffuser);
        }
    }
}

В заключение, этот стиль развития BDD/TDD. Это заняло один год, но я превращаюсь в образ жизни как образ жизни. Я бы не узнал об этом сам. Я взял все из Oracle http://orthocoders.com/.

Красная или синяя пилюля, выбор за вами.

Ответ 10

Прочтите тестовый код!

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

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

Ответ 11

Хотя мы никогда не могли реализовать полный TDD, концепция сообщения о дефекте и создание тестового примера phpunit (в нашем php-магазине), который потерпел неудачу, но пройдет, когда будет устранен дефект, оказалось желательным для всех сторон (dev и qa) из-за ясности спецификации дефекта и проверки измененного кода. Мы также включили эти модульные тесты в набор регрессии, если мы пропустили эти ветки "исправления дефектов" в выпущенном коде.

Ответ 12

TDD работает лучше и лучше, чем больше у вас опыта. Но сложно достичь уровня безубыточности, что облегчает выполнение tdd вместо tad.

так что противоположный вопрос: "Что мешает мне делать tdd" может помочь получить хорошую отправную точку:

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

мотивация приходит ко мне, если блага возмущают боль.

В настоящее время я разрабатываю детали в java/tomcat/web shop, где для компиляции всех и запуска серверов/магазинов требуется около 10 минут, что является противоположностью быстрого развертывания приложений.

Компиляция и запуск businesslogic и unittests требует менее 10 секунд.

Таким образом, tdd намного быстрее, чем tad, если просто написать блок-тест.

тот же самый рад относится к моему текущему проекту Android, где я разрабатываю независимую часть андроида java lib, которая может быть легко unittested. Нет необходимости разворачивать код на устройстве, чтобы узнать, работает ли код.

По моему мнению, хорошей отправной точкой для получения опыта с tdd: дождитесь появления нового приложения зеленого поля, где у вас нет давления времени в начале.