В TDD я должен сначала написать Test или Interface?

23

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

Но также сказано, что « Программа для интерфейса, а не для реализации », поэтому сначала напишите интерфейс . Здесь начинается мое замешательство. Если я сначала пишу интерфейс, то это нарушает две вещи

  1. Код, написанный для интерфейса, не управляется тестом .

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

Должен ли я начать с написания тестов для интерфейса также? без какой-либо реализации, что я собираюсь проверить?

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

k4vin
источник
8
«Программа для интерфейса» означает отделение того, что вам нужно, от куска кода от того, как это делается. Это не значит буквально использовать interfaceдля всего. А classтакже предоставляет интерфейс, потому что вы можете скрыть детали реализации в privateпеременных.
Доваль
@Doval, да, вам не нужен интерфейс для всего, только то, что называется contract. Это может быть, например, абстрактный класс, хотя это не должен быть виртуальный класс / метод, потому что вы не сможете создать его экземпляр.
trysis
2
TDD говорит: «Напиши тест, который провалился». Некоторые строгие TDD говорят, что это считается «неудачей», если вы не можете скомпилировать тест, потому что тип данных, с которыми он работает, еще не объявлен.
Соломон Медленный

Ответы:

29

Ваше первое нарушение («Код, написанный для интерфейса, не проверяется тестом») недействителен. Давайте использовать тривиальный пример. Предположим, вы пишете класс калькулятора, и вы пишете операцию сложения. Какой тест вы могли бы написать?

public class CalculatorTest {
    @Test
    public void testAddTwoIntegers() {
        Calculator calc = new Calculator();
        int result = calc.add(2, 2)
        Assert.assertEquals(4, result);
    }
}

Ваш тест только что определил интерфейс. Это addметод, понимаете? addпринимает два аргумента и возвращает их сумму. Позже вы можете определить, что вам нужно несколько калькуляторов, и извлечь (в данном случае) интерфейс Java в это время. Тогда ваши тесты не должны меняться, так как вы тестировали открытый интерфейс этого класса.

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

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

Майкл К
источник
2
+1 « Я не думаю, что вы можете отделить дизайн интерфейса от тестового дизайна » должно быть выделено жирным шрифтом, ИМХО :)
Binary Worrier
Это еще проще показать, если вы хотите протестировать более одной реализации функциональности, например, «Импорт XML» и «Импорт CSV», вы можете протестировать их одним и тем же методом из того же интерфейса, хотя реализация изменится. Кроме того, тесты часто включают в себя некоторые насмешки, и для этого интерфейса необходимы.
Вальфрат
Вы говорите, что тест не нужно менять, но new Calculator()правильна ли реализация? Если требуется новая реализация, может быть, вы бы тогда сделали MultiplicationCalculator, и вам нужно было бы изменить тест, чтобы new AdditionCalculator()он по-прежнему проходил? Или я что-то упустил?
Стив Chamaillard
3
@SteveChamaillard Конечно, если в вашем проекте вы измените имя класса с Калькулятора на AdditionCalculator, тест должен измениться, чтобы соответствовать. Конечно, при выполнении TDD на самом деле вы должны сначала изменить тест, а затем выполнить изменение класса, чтобы пройти тест.
Эрик Кинг,
5

Что мы делаем, когда пишем interface? Мы пишем код или мы проектируем?

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

Я остановлюсь прежде, чем начну разглагольствовать, но, надеюсь, это поможет вам подумать об этом.

Резиновая утка
источник
4

В TDD я должен сначала написать Test или Interface?

Все зависит от того, насколько православным / религиозным вы хотите заниматься TDD .

Я учусь TDD

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

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

  2. Помимо TDD или нет: вопрос, использовать интерфейс или нет, в первую очередь не интересен. Конечно, если вы уверены, что у вас разное поведение, которое вы хотите распределить по нескольким объектам, имеет смысл подумать об использовании интерфейса: например, если у вас есть какой-то вывод в разные пункты назначения, имеет смысл реализовать его через Интерфейс Writer и имеют разные классы для вывода ( FileWriter , Printer и т. д.). Хотя это обычное высказывание - писать в интерфейс , но это не значит: использовать интерфейс для всего . Иногда это один уровень косвенности для многих. Btw. То же самое касается услуг. Но это другая тема.

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

Томас Джанк
источник
5
Не могу согласиться с последним пунктом «Неважно, если вы пишете тесты до или после, если вы все равно тестируете». Если вам случится написать тест впоследствии, вы не можете быть уверены, что тест проверяет правильность.
k4vin
4
Как я уже сказал ... Это зависит от того, насколько ты православный ...
Томас Джанк,
2

TDD или BDD означали бы сначала выполнение интерфейсов вашего домена, а затем написание тестов на них по моей интерпретации. Реализация интерфейса имеет ожидаемое поведение.

он все еще тестируется перед кодом, потому что интерфейс не содержит тестируемой логики, это структура, против которой вы пишете тест.

Я бы сделал это следующим образом

  1. Напишите Полу Формальное Поведение (Дано: Когда: Тогда :)

  2. Напишите интерфейс (для размещения метода инкапсуляции поведения)

  3. Напишите тест, который он идентифицирует (введите данное, вызовите когда, протестируйте затем)

  4. Записать / изменить бетон (класс, реализующий интерфейс) для прохождения теста

user3721330
источник
0

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

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

Ниссим Леви
источник
рассуждения в верхнем ответе выглядят более убедительно: «Я не думаю, что вы можете отделить дизайн интерфейса от дизайна теста. Определение взаимодействий и разработка тестов для них - это одно и то же умственное действие - когда я отправляю эту информацию в интерфейс, я ожидаю определенного результата . Когда что-то не так с моим вводом, я ожидаю эту ошибку ... "
gnat
@gnat Я полагаю, что здесь Nissam ссылается на interfaceключевое слово C # , а не на общий термин «интерфейс».
RubberDuck
@RubberDuck Я тоже так думаю и считаю, что это плохой подход. «Пока вы этого не сделаете, вы не должны думать о тестах ...» Как я уже писал, рассуждения в верхнем ответе выглядят более убедительно, как в общем смысле интерфейса, так и в смысле конкретного ключевого слова
gnat
Достаточно справедливо @gnat, что не было ясно из вашего комментария. Лично я согласен с Nissam здесь. Я считаю, что это мешает людям использовать TDD в качестве предлога, чтобы вообще не проектировать. YMMV.
RubberDuck
-2

Можно писать интерфейс / код / ​​тест одновременно, если их включение в проект является атомарным.

Если ваш начальник не верит в TDD, в этом случае вам, вероятно, придется написать пустой интерфейс -> test -> минимальный код (бессмысленный шаг) -> больше тестов -> больше бессмысленного кода -> больше тестов -> наконец написать реальный код - > сделано.

альтернатива
источник