Как/, Вы “вынуждаете” себя сделать TDD, а не НЕМНОГО? [закрытый]

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

37
задан Piotr Owsiak 28 April 2010 в 16:06
поделиться

9 ответов

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

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

3
ответ дан 27 November 2019 в 04:28
поделиться

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

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

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

3
ответ дан 27 November 2019 в 04:28
поделиться

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

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

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

21
ответ дан 27 November 2019 в 04:28
поделиться

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

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

26
ответ дан 27 November 2019 в 04:28
поделиться
  1. Будет полезно, если у вас есть общая среда тестирования.

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

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

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

  2. Используйте подход «тест как документация». Не добавляйте / не изменяйте какие-либо формулировки в документации, не подтвержденные соответствующими тестами.

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

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

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

  4. Сразу выделите время для написания тестов в начале вашего графика разработки по проекту

    Это заставит вас выработать правильные привычки (при условии, что вы склонны следовать плану проекта) и защитит вас от превышения сроков выполнения из-за «лишнего» времени, потраченного на испытания сборки.

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

  5. Также старайтесь следовать обычным рекомендуемым методам работы с небольшими одноцелевыми классами и функциями. Это - среди всех других преимуществ - позволяет значительно упростить написание модульных тестов. Соедините это с №2 (написав модульный тест как часть документации API при разработке API), и ваши проекты API «волшебным образом» улучшатся, поскольку вы сразу начнете замечать слабые места из-за написания тестов, основанных на них. Как отмечали другие люди, использование какого-то шаблона / фреймворка внедрения зависимостей помогает упростить создание тестов.

5
ответ дан 27 November 2019 в 04:28
поделиться

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

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

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

4
ответ дан 27 November 2019 в 04:28
поделиться

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

8
ответ дан 27 November 2019 в 04:28
поделиться

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

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

6
ответ дан 27 November 2019 в 04:28
поделиться

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

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

Год Раньше я плохо понимал, как делать TDD (но очень хотел (как обидно)) и никогда не слышал о BDD ... теперь я навязчиво делаю и то, и другое. Я работал в среде разработки .Net, а не на Java, но я даже заменил кнопку «F5 - Выполнить» макросом для запуска Cucumber (BDD) или MBUnit (TDD) в зависимости от того, является ли это функцией / сценарием или спецификацией. Никакого отладчика, если это вообще возможно.1 доллар в банке, если вы используете отладчик (ШУТКИ (вроде)).

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

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

Функция:

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

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

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

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

Пример:

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

Существует еще одна спецификация (файл класса C # 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 строк заглушки. Это очень простой пример без GivenThat () или AndGivenThatAfterCreated () в этой спецификации.

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

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

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

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 с жесткими зависимостями и т. Д., Вы почувствуете боль от попыток выполнить TDD и будете сыты по горло / сдадитесь ИЛИ укусите пулю и сделаете это правильно.

А вот собственно SUT. У нас есть немного фантазии и мы используем PostSharp для добавления уведомления об изменении свойства в диффузоре,поэтому 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 очень важен. На это потребовался год, но я полностью обращаюсь в свою веру. Я бы не научился этому самостоятельно. Я взял все из Оракула http://orthocoders.com/ .

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

2
ответ дан 27 November 2019 в 04:28
поделиться
Другие вопросы по тегам:

Похожие вопросы: