Создает ли объекты, которые, по вашему мнению, вам понадобятся в первом тесте в TDD?


15

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

Например, в моем Github ConwaysGameOfLifeExample первый тест, который я написал (rule1_zeroNeighbours), я начал с создания объекта GameOfLife, который еще не был реализован; вызывал метод set, который не существует, метод step, который не существовал, метод get, который не существовал, а затем использовал assert.

Тесты развивались, когда я писал больше тестов и проводил рефакторинг, но изначально это выглядело примерно так:

@Test
public void rule1_zeroNeighbours()
{
    GameOfLife gameOfLife = new GameOfLife();
    gameOfLife.set(1, 1, true);
    gameOfLife.step();
    assertEquals(false, gameOfLife.get(1, 1));
}

Это было странно, так как я заставлял проект реализации основываться на том, как я решил на этом раннем этапе написать этот первый тест.

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

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


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

Ответы:


9

Это было странно, так как я заставлял проект реализации основываться на том, как я решил на этом раннем этапе написать этот первый тест.

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

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

Pro Upfront Design

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

Pro Test-Driving Design

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

  • Идея нарисовать кучу диаграмм UML перед написанием любого кода, а затем просто заполнить пробелы, хороша, но редко реалистична. В «Полном коде» Стива Макконнелла дизайн, как известно, описывается как «злая проблема» - проблема, которую вы не можете полностью понять, если сначала хотя бы частично ее не решить. Соедините это с тем фактом, что сама проблема может измениться в результате изменения требований, и эта модель дизайна начинает казаться немного безнадежной. Тестовое вождение позволяет вам просто откусить один кусок работы за раз - в дизайне, а не только за реализацию - и знать, что, по крайней мере, в течение срока службы от красного до зеленого эта задача все еще будет актуальной и актуальной.

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


Ссылка была очень хорошо прочитана Беном. Спасибо, что поделились этим.
RubberDuck

1
@RubberDuck Не за что! Я не совсем согласен с этим, на самом деле, но я думаю, что он отлично справляется с этой точкой зрения.
Бен Ааронсон

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

17

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

Из практического юнит-тестирования с JUnit и Mockito :

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

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

Если бы я написал это приложение, я бы подумал о том, что я хочу построить. Например, я мог бы создать Cellкласс, написать тесты для этого, прежде чем перейти к более крупному приложению. Я бы, конечно, создал класс для структуры данных «бесконечный во всех направлениях», который требуется для правильной реализации Conway, и протестировал бы это. Как только все это будет сделано, я подумаю о написании общего класса, который имеет mainметод и так далее.

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


1
Учитывая, что Cell будет просто обёрткой boolean, такой дизайн, безусловно, будет хуже для производительности. Разве он должен быть расширяемым в будущем для других клеточных автоматов с более чем двумя состояниями?
user253751

2
@immibis Это спор о деталях. Вы можете начать с класса, который представляет коллекцию ячеек. Вы также можете перенести / объединить класс ячейки и его тесты с классом, представляющим коллекцию ячеек позже, если производительность является проблемой.
Eric

@immibis Количество живых соседей может быть сохранено по соображениям производительности. Количество тиков, в которых клетка была жива, по причинам окраски ..
Blorgbeard вышел

@immibis преждевременная оптимизация - это зло ... Более того, избегание примитивной одержимости - отличный способ написать код хорошего качества, независимо от того, сколько состояний он поддерживает. Взгляните на: jamesshore.com/Blog/PrimitiveObsession.html
Пол

0

Там разные школы мысли об этом.

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

Некоторые говорят: все в порядке, чтобы написать тест, сначала проверить, сосет ли он (или нет), а затем создать недостающие классы / методы.

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

Вам решать, каким образом вы будете работать. ИМХО оба подхода верны.


0

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

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

Что может помочь вам, это использовать Cucumber или подобное для написания ваших тестов.


0

Прежде чем вы начнете писать свои тесты, вы должны подумать о том, как спроектировать вашу систему. Вы должны потратить значительное количество времени на этапе проектирования. Если вы сделали это, вы не получите эту путаницу из-за TDD.

TDD - это просто ссылка на подход к разработке : TDD
1. Добавьте тест
2. Запустите все тесты и посмотрите, не прошел ли новый тест
3. Напишите некоторый код
4. Запустите тесты
5. Код рефакторинга
6. Повторите

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


0

По этой причине я не люблю тесты системного уровня, написанные на Java или C #. Посмотрите на SpecFlow для c # или один из основанных на Cucumber тестовых фреймворков для Java (возможно, JBehave). Тогда ваши тесты могут выглядеть примерно так.

введите описание изображения здесь

И вы можете изменить свой объектный дизайн без необходимости менять все системные тесты.

(«Нормальные» юнит-тесты хороши при тестировании отдельных классов.)

Каковы различия между средами BDD для Java?

Используя наш сайт, вы подтверждаете, что прочитали и поняли нашу Политику в отношении файлов cookie и Политику конфиденциальности.
Licensed under cc by-sa 3.0 with attribution required.