Як я можу почати використовувати TDD для кодування простої функціональності?


9

Я в основному суть TDD. Мені продають, що це корисно, і я розумно володію рамою MSTEST. Однак на сьогоднішній день мені не вдалося перейти на використання його як основного методу розвитку. Здебільшого я використовую це як сурогат для написання консольних додатків як тестових драйверів (мій традиційний підхід).

Найбільш корисне для мене це те, як він поглинає роль тесту регресії.

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

Отже, це запитання полягає в тому, щоб задати вказівки на те, які перші тести (тести) я можу написати для наступного завдання на розробку: Я хочу створити код, який інкапсулює виконання завдання в режимі виробника / споживача.

Я зупинився і вирішив написати це питання після того, як я написав цей код (цікаво, чи можу я реально використовувати TDD реально цього разу)

Код:

interface ITask
{
    Guid TaskId { get; }
    bool IsComplete { get; }
    bool IsFailed { get; }
    bool IsRunning { get; }
}

interface ITaskContainer
{
    Guid AddTask(ICommand action);
}

interface ICommand
{
    string CommandName { get; }
    Dictionary<string, object> Parameters { get; }
    void Execute();
}

Ви повинні були написати тести спочатку, а потім інтерфейси! Вся ідея полягає в тому, що TDD призначений для вашого API.

1
Як можна написати тести для інтерфейсів, які ще не існують? Вони навіть не збираються.
Роберт Харві

5
Це ваш перший невдалий тест.
cori

Відповіді:


10

Починаючи з цієї концепції:
1) Почніть з тієї поведінки, якої ви бажаєте. Напишіть для нього тест. Дивіться невдачу тесту.
2) Напишіть достатньо коду, щоб пройти тест. Переглянути всі проходження тестів.
3) Шукайте зайвий / неохайний код -> рефактор. Дивіться тести ще проходять. Перейти 1

Отже, на # 1 скажімо, що ви хочете створити нову команду (я тягнуся до того, як команда працювала, тому поводьтеся зі мною). (Також я буду трохи прагматичнішим, а не екстремальним TDD)

Нова команда називається MakeMyLunch, тому ви спершу створите тест, щоб інстанціювати її та отримати ім'я команди:

@Test
public void instantiateMakeMyLunch() {
   ICommand command = new MakeMyLunchCommand();
   assertEquals("makeMyLunch",command.getCommandName());
}

Це не вдається, змушуючи вас створити новий клас команд і дати йому повернути свою назву (пурист сказав би, що це два раунди TDD, а не 1). Отже, ви створюєте клас і надаєте йому реалізувати інтерфейс ICommand, включаючи повернення імені команди. Запуск усіх тестів тепер показує всю прохідність, тому ви продовжуєте шукати можливості рефакторингу. Напевно, жодного.

Тож далі ви хочете, щоб він виконав Execute. Тож ви повинні запитати: як я знаю, що "MakeMyLunch" успішно "зробив мій обід". Які зміни в системі через цю операцію? Чи можу я перевірити це?

Припустимо, це легко перевірити на:

@Test
public void checkThatMakeMyLunchIsSuccessful() {
   ICommand command = new MakeMyLunchCommand();
   command.execute();
   assertTrue( Lunch.isReady() );
}

В іншому випадку це складніше, і те, що ви насправді хочете зробити, - це перевірити обов'язки суб'єкта, що перебуває під тестом (MakeMyLunchCommand). Можливо, відповідальність MakeMyLunchCommand полягає у взаємодії з холодильником та мікрохвильовою піччю. Тому для тестування ви можете використовувати макет Холодильник та знущатися з Мікрохвильовки. [дві зразкові схеми макету - Mockito і nMock або дивіться тут .]

У такому випадку ви зробите щось на зразок наступного псевдокоду:

@Test
public void checkThatMakeMyLunchIsSuccessful() {
   Fridge mockFridge = mock(Fridge);
   Microwave mockMicrowave = mock(Microwave);
   ICommand command = new MakeMyLunchCommand( mockFridge, mockMicrowave );
   command.execute();
   mockFramework.assertCalled( mockFridge.removeFood );
   mockFramework.assertCalled( microwave.turnon );
}

Пурист каже перевірити відповідальність вашого класу - його взаємодії з іншими класами (чи команда відкрила холодильник і включила мікрохвильову піч?).

Прагматик каже тест на групу занять і тест на результат (чи готовий ваш обід?).

Знайдіть правильний баланс, який працює для вашої системи.

.


якби я не заздалегідь написав інтерфейс, яке питання призвело б до створення методу Execute () - деякі мої початкові спроби TDD застопорилися, коли у мене не було "кроку" для стимулювання додаткової функціональності - я отримую почуття є прихованою проблемою з куркою / яйцем, яку потрібно пройти стороною
Аарон Анодід,

1
Хороше питання! Якщо єдиною командою, яку ви зробили, було "MakeMyLunchCommand", метод, можливо, почався з ".makeMyLunch ()". Що було б добре. Потім ви робите іншу команду ("NapCommand.takeNap ()"). Досі проблем із різними методами немає. Потім ви почнете використовувати його у вашій екосистемі, імовірно, там, де вас змушують генералізувати в інтерфейсі ICommand. Взагалі, ви часто затримуєте генералізацію до останнього відповідального моменту, тому що YAGNI [ en.wikipedia.org/wiki/You_ain't_gonna_need_it ] =) Інші часи ви знаєте, що збираєтесь потрапити туди, щоб почати з цього.
jayraynet

(Також тут припущення полягає в тому, що ви використовуєте сучасний IDE, який робить рефакторинг таких речей, як імена методів тривіальні)
jayraynet

1
ще раз дякую за пораду, для мене це якась віха, що я нарешті побачив усі шматки та те, як вони підходять - і так, швидкий рефактор є в моєму
наборі
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.