Вдохновение для юнит-тестов

  • Tutorial
Много слов сказано о достоинствах юнит-тестов (TDD, BDD — в данном случае неважно), а также о том, почему люди всё-таки их не используют.

Но я думаю, что одна из главных причин заключается в том, что люди не знают, с чего начать. Вот прочитал я статью про юнит-тесты, понравилось; решил, что надо бы когда-нибудь попробовать. Но что дальше? С чего начать? Как придумывать все эти требования, как называть тест-методы?

В последнее время набирает популярность тенденция превращать юнит-тесты в BDD-спецификации, то есть говорится о том, что хороший юнит-тест должен не тестировать что-то, а описывать поведение программы. Но как описать это чёртово поведение; откуда брать вдохновение, чтобы придумать названия для всех этих тест-кейсов?

Об этом и пойдёт речь:

Откуда брать вдохновение



Однажды я пришёл к удивительному открытию: это вдохновение — повсюду, оно окружает нас каждый день. Тут и придумывать ничего не надо. Его можно просто брать и использовать. Это открытие показалось мне настолько важным, что я спешу непременно поделиться им со всеми.

Итак, мой TOP-5 источников вдохновения для написания юнит-тестов.

5. Доклад начальнику


Каждое утро на собрании начальник спрашивает тебя: «Что ты вчера делал?».
— Багу исправлял.
Начальник так просто на слово не верит и продолжает докапываться:
— Какую багу?
— Ну, исправил метод validateReferenceNumber.
— Конкретнее, что ты там исправил?
— Ну, раньше он грохался, если дать ему на входе пустую строку, а теперь он не падает, а возвращает false.


Gotcha!

Видите, практически готовый юнит-тест висит в воздухе у вас перед носом. После собрания садитесь за компьютер и пишете:
public class ReferenceNumberTest {
  @Test
  public void emptyStringIsNotValidReferenceNumber() {
    assertFalse(validateReferenceNumber(""));
  }
}


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

4. Объяснения с коллегами



К вам каждый день приходит какой-нибудь коллега и спрашивает:
— Слушай, я тут твой код дебажу-дебажу, а всё никак не могу понять, как это работает?
Ты ему терпеливо объясняешь:
— Ну как, ну смотри: сюда приходит Б, а отсюда выходит А.
— Всё равно не понимаю, а почему А-то?
— Ну потому, что для всех букв, которые меньше Я, этот метод должен возвращать следующую букву.
— Ааа, вот теперь понятно.


И снова Gotcha!

Вы, сами того не подозревая, только что сформулировали название тест-кейса.
Мысленно послав +1 в карму коллеги, сели и написали:
public class NanoTechnologySecurityTest {
  @Test
  public void shouldReturnNextLetterForAllLettersExceptJa() {
    assertEquals("Б", encodeLetter("А"));
  }
}


3. Разговор с клиентом


В любой прекрасный день клиент может позвонить и сказать:
— Помните, мы обсуждали, что все поля на форме должны валидироваться автоматически, как только поле теряет фокус? Так вот, я передумал. Я показывал бета-версию моей бабушке, и она сказала, что это не понятно, и вообще ajax sucks. Давайте поля будут валидироваться только тогда, когда клиент нажмёт кнопку «Submit».

И опять Gotcha!

Клиент только что сформулировал за нас текст тест-кейса.

Мысленно послав -10 в карму клиента, мы сели и написали… UI тесты, конечно, чуток сложнее, чем обычные юнит-тесты, но это могло бы выглядеть как-то так:

public class TimotiFanClubRegistrationFormTest {
  @Test
  public void shouldValidateFieldsOnFormSubmission() {
    Form form = new Form();
    assertTrue(form.isValid());

    form.submit();
    assertFalse(form.isSubmitted());
    assertFalse(form.isValid());

    form.setName("Baba Njura");
    form.setEmail("Baba.Njura@yandex.ru");
    form.submit();
    assertTrue(form.isSubmitted());
  }
}


2. Баг


Каждый день вы заходите в Jira (а кому совсем повезло — в Pivotal Tracker) и обнаруживаете, что в вашей программе зловредные тестировщики нашли-таки багу. Если вам повезло с тестировщиком, то описание баги звучит примерно так: «Если ввести три раза неправильный пароль, то учётная запись должна заблокироваться, а у меня не блокируется. Я уже раз пятнадцать ввёл.»

Мысленно сказав тестировщику спасибо (хватит с него и спасиба, его карму всё равно не спасёшь), садимся и пишем:
public class LoginPageTest {
  @Test
  public void shouldBlockAccountAfter3UnsuccessfulTries() {
    LoginPage page = new LoginPage();
    page.login("vasjok47", "Toiota");
    page.login("vasjok47", "Tojota");
    page.login("vasjok47", "tayota");
    assertTrue(AccountDAO.getAccount("vasjok47).isBlocked());
  }
}


Есть даже отдельный термин для этого: Bug driven development. Я сам этот подход не жалую, так как юнит-тесты (они же спецификация) всё-таки должны писаться ДО кода (основной принцип TDD), но как источник вдохновения баг-трекер вполне подходит.

1. Commit message (как это по-русски?)


Это мой любимый пункт, на нём я хотел бы остановиться подробнее.

Когда вы меняете код, для этого обычно есть причина. Причина обычно заключается в том, что вы хотите, чтобы этот код что-то делал по-другому (исключаем рефакторинг, улучшение производительности и расставление скобочек по фэншую).

Допустим, был у нас код, который валидировал email. В частности, он проверял, что в конце email должна быть точка и два символа. И даже есть для него юнит-тест, всё как у людей. И тут внезапно выясняется, что после точки может быть и больше букв, например, у некоторых клиентов email заканчивается на ".info". Сказано-сделано, код исправили, и даже в существующий юнит-тест добавили одну строчку:

public class EmailValidatorTest {
  @Test
  public void testValidateEmail() {
    assertTrue(validateEmail("timati@rambler.ru"));
    ...
    assertTrue(validateEmail("tina.turner@music.info")); // 4 letters now allowed!
  }
}


И теперь хотим это дело закоммитить в CVS (а кому повезло, те в SVN, а кто вообще счастливчик, те в GIT).
Для коммита надо написать пояснение (commit message), в котором обычно пишут, а что же в коде изменилось. И вот пишите вы:
svn commit -m "Теперяча мыло и на четыре буквы может заканчиваться."


И вот это самая настоящая Gotcha!

Это то, что нам надо. Не нажимайте пока enter.
Остановились, выделили этот текст мышкой. Открыли класс юнит-теста. Написали Test, вставили скопированный текст и перевели на английский. Можно немножко уточнить или обобщить по вкусу.
public class EmailValidatorTest {
  @Test
  public void validEmailShouldEndWithDotFollowedBySeveralLetters() {
    assertTrue(validateEmail("timati@rambler.ru"));
    ...
    assertTrue(validateEmail("tina.turner@music.info"));
  }
}


Вот теперь можете смело закоммитить и мысленно послать +1 себе в карму. Сегодня мы смогли материализовать некоторые знания из воздуха во что-то более ощутимое. Теперь эти знания никуда не пропадут и будут автоматически проверяться при каждой сборке проекта.

Ну не здоровско ли, а?

Share post

Comments 50

    +2
    Кстати, написание юнит-тестов к готовому коду — один из лучших способов изучить этот самый код. Принимая на работу человека, мы даем ему от 2 недель до 2 месяцев на написание тестов для отдельных частей системы. Работает очень хорошо.

      +7
      Это если повезёт :-) Для того, чтобы можно было протестировать код — изначально код должен быть написан с расчётом на то, что он будет когда-то покрыт тестами, ну или просто быть достаточно качественным.
        0
        Согласен.

        Юнит-тесты у нас есть — и всегда можно найти, что еще бы потестировать. Тут почти нет пределов — возможно, это специфика области (движок базы данных)

        А идти в проект, где куча кода и нет тестов — это как плохому учиться: легко и просто, но и пользы никакой.
          +5
          А вот тут Вы не правы.
          В данном случае можно научиться пошаговому рефакторингу из самых ужасных ситуаций.
            +2
            Это ваш опыт за плечами говорит.

            Бывает код, который можно рефакторить-рефакторить, да не вырефакторить. В таких случаях нужен профессионал, который сам многому научит, который придет и перепишет «как надо».

            А студента, у которого опыта с пару курсовых, так «человеком» не сделать, нужно хорошие примеры: код, тесты, процесс, обучение.
              +3
              Я даже не знаю, кто из вас сейчас сильнее прав. Я сейчас работаю на проекте, который изначально был… Мм… Фаршем. Простые примеры:
              — ни одного теста. То есть, вообще. Совсем. Тупо нет и всё.
              — код пестрит чем-то типа String Rabota = «1»; это такое, до сих пор не очень понял
              — коммиты почти всегда идут без комментов
              — иногда натыкаешься на 200 строк закомментированого кода и не понятно что это, зачем и почему оставлено
              — ну и так далее. Все подробности описывать не буду.
              Опыта у меня довольно мало, чуть больше, чем пол года. Но на предыдущем месте работы всё было «по феншую»: начиная от багтракера, которого тут тупо нет, заканчивая строгим Code Policy.
              В общем, к чему это я… начинал изучение этого самого проекта я именно с тестов, в смысле, что сам писал. Помимо того, что начал хоть немного врубаться, как это устроено, нашел кучку багов, которые тут же фиксил. Но для того, чтобы привести тут всё в порядок — надо просто всё переписывать. С нуля.
                0
                Суть подхода и опыт работы в таком процессе у вас есть, это хорошо. Теперь вам надо двигаться в сторону работы с legacode, его модернизации, тестирование и рефакторинг. Процесс не легкий. Иногда кажется что все выкинуть и написать с нуля будет лучшем решением, но это не так.
                  0
                  | legacode
                  А можно чуть подробнее? Куда копать, что почитать, и т.д. Впервые слышу подобный термин :(
                    0
                    Сори, может ввел немного в заблуждение. Я имел в виду legacy code. Обычно так называют код который достался в наследство :) И имеет ряд недостатков:
                    — Например такой код тяжело модернизировать.
                    — Такой код тяжело поддается тестированию.
                    — Код работает но чтобы понять почему, уходить много времени
                    — и так далее.
                    Думаю начать нужна с этой книги Michael Feather «Working Effectively with Legacy Code». Далее великий интернет вам поможет :)
        +1
        Ну вообще написание тестов по готовому коду немного противоречит TDD, тесты же должны писаться до разработки функционала. Но соглашусь с тем, что это в идеале, т.к. очень часто приходится дописывать неучтенные тесты, или даже внедрять тестирование на уже запущенных проектах.

        А по-поводу новых сотрудников мы работали несколько иначе. Приходил человек, мы его подсаживали в пару к другому разработчику, как результат человек за 2 — 3 недели въезжал в проект, а также быстрее вливался в коллектив =)
          0
          Ну, по этому поводу тоже много кто писал. XP, парное программирование )) У нас на старой работе ребята как-то чуть не подрались, сидя за одним компом )) До сих пор вспоминаем :)
            +2
            И пускай кто-то скажет, что парное программирование не экстремально :)
              0
              А у нас на работе все программируют в пара, и это отлично работает. Это как доказательство того, что XP — это хорошо. А тот факт, что у вас это один раз не получилось, вовсе не доказывает, что XP — это плохо.
                0
                Да ладно Вам) Я же не сказал, что это хорошо или плохо. Просто случай из жизни рассказал. Вообще, если я правильно помню, то TDD — это один из подходов XP («Extreme programming explained», Kent Beck).
          +2
          Кстати да! Хорошие способы, но вопрос «с чего же начать?» так и не раскрыт, на мой взгляд. Помню себя в те мучительные времена становления вопросов ТДД в голове и могу только посоветовать начинающим курить эту классику до тех пор, пока в голове не «щелкнет». Лично мне только она помогла.
            0
            Начинать полезно с интерфейсов )

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

            Это хорошо работает для обособенной логики. Если же нужно протестить Web, EJB и т.д., то используем мощь соответствующего framework'а. Как правило, сейчас даже в мануалах есть разделы «как это тестировать».
            +1
            Здоровски! :) Читается просто на одном дыхании. Действительно, самое сложное — это начать писать тесты. И по моему мнению, Вы отлично показали как это можно сделать. Спасибо!
              0
              Толково расписано, понятным языком.
                +3
                Расписано, безусловно, толково, но это не TDD. Как было верно указано в начале статьи, тест должен не тестировать, а описывать поведение программы. Т.е. тест должен придумываться задолго до коммита или вопроса начальника. Написали тест — написали код — подтвердили прохождение теста — закоммитили.
                Сами тесты же должны браться из ТЗ, спеки или переговоров с заказчиком. Вы же, в конце концов, знаете, что должна делать ваша программа — ну так и описывайте. Лично для себя я выбрал метод «от общего к частному». Т.е. сначала я описываю общий функционал отдельного модуля/страницы, затем, по выполнении общих тестов, описываю мелкие детали и частные случаи. Если эти тесты валятся — правлю код. Это очень удобно, т.к. не требует изначального утомительного описывания каждого чиха программы.
                  0
                  хорошая статья, еще раз убедился принял правильное решение о написании юнит тестов в своей программе
                    +4
                    Уже, по-моему, 3-я статья за последние пару недель о юнит-тестах, где путается тёплое с мягким: заголовок про юнит-тесты, обязательное упоминание TDD, а затем и функциональные-приёмочные тесты до кучи (при этом ни слова о подходе самого TDD).
                      –1
                      > Каждое утро на собрании начальник спрашивает тебя: «Что ты вчера делал?».
                      > — Багу исправлял.
                      Автор целый день исправлял багу в одном методе! Налицо эффективность TDD! Это не просто язвительное замечание. Производительность людей, зацикленных на TDD, реально такая. И ничего сложней валидатора они как правило не пишут. Вы хоть примеры-то для разнообразия поинтересней придумайте, а то одни валидаторы…

                      > — Слушай, я тут твой код дебажу-дебажу, а всё никак не могу понять, как это работает?
                      Так надо было по-русски прямо в коде и написать:

                      /** Энкодит букву по алгоритму:…
                      Например если на вход подать А, то получится Б */
                      public String encodeLetter( String letter )

                      Да-да, прямо в коде, не надо бояться! И ошибки не будет, и лишний тест не надо писать, и коллега сразу сообразит что к чему. Это называется контрактом. А в Вашем случае прекрасно сработает и это:
                      public String encodeLetter( String letter ) { return «Б» }
                      И будет соответствовать «спецификации».

                      > assertTrue(validateEmail(«tina.turner@music.info»)); // 4 letters now allowed!
                      Где спецификация самого метода? С каких хренов 4 буквы not allowed? Где это написано? При чем тут тесты? А 5 буквами как дела обстоят? А с доменом-поддоменом? Рановато +1 в карму…
                        +5
                        Откуда столько ненависти?
                        Да, это нормально исправлять целый день один метод, даже если вы не пишете юнит-тесты, просто методы и их сложность бывают разные. В статье простой и подходящий пример — валидация.
                        Нет, не нужно в коде писать по-русски, это же код а не художественный роман, пишите на английском хотя бы.
                        0
                        Откуда столько ненависти?
                        Да, это нормально исправлять целый день один метод, даже если вы не пишете юнит-тесты, просто методы и их сложность бывают разные. В статье простой и подходящий пример — валидация.
                        Нет, не нужно в коде писать по-русски, это же код а не художественный роман, пишите на английском хотя бы.
                          –3
                          Откуда столько ненависти?
                          Да, это нормально исправлять целый день один метод, даже если вы не пишете юнит-тесты, просто методы и их сложность бывают разные. В статье простой и подходящий пример — валидация.
                          Нет, не нужно в коде писать по-русски, это же код а не художественный роман, пишите на английском хотя бы.
                            +1
                            Да, я ошибся формой для ответа, подлежу расстрелу
                              –1
                              Между прочим, такие ошибки зачастую мемы порождают)
                          +1
                          а у меня не получается писать юнит-тесты:
                          1. Их обычно пишут на public методы, а у меня public метод только один для класса. Все остальные private
                          2. Подготовить данные для тестирования бизнес-логики настоящая проблема, потому что надо сгенерировать сложнейшие связи в базе данных, которые зависимы друг от друга.
                          3. Написать скрипт длиной в несколько сотен строк, генерирующий данные, мне просто знаний sql не хватит.
                          4. Объяснять спецу по базам данных что мне надо, а потом еще 20 раз попросить исправить что-то в скрипте — займет 90% от выделенного мне на задачу времени.
                          5. Иметь каждому разработчику отдельную базу и синхронизировать у нас не принято, да и не удобном опять же по причине синхронизации и написания полотен sql скриптов.

                          Вот и выходит, что тестировать не удобно юнит тестами, а учитывая, что мы используем не простые sql запросы, а fluent nhibernate, то создать объект внутри базы становится непосильной задачей, т.к. объект не будет записан в базу (ссылка на null в базе) до тех пор, пока мы создадим еще 100500 подобъектов

                          Писать тесты для валидации или работа со строками — это просто, а что делать со связями в БД не знаю.

                          А я очень хочу писать тесты, но как решить проблему не знаю.
                            0
                            В таких случаях используются фабрики и генераторы фейковых объектов для наполнения тестовой базы. Да, кстати, о тестовой базе. Она должна быть в любом случае.
                            В инструментах на PHP, к сожалению, не искушен, но можете посмотреть FFaker и Fabricator для Ruby, чтобы понять, о чем я говорю.
                              0
                              1. Их обычно пишут на public методы, а у меня public метод только один для класса. Все остальные private

                              Не функции покрываются, а их код. Тестируйте публичный метод, но так чтобы каждая приватная (точнее каждый блок каждой функции) был бы вызван. Если бы использовали TBC то так бы и вышло.
                              2. Подготовить данные для тестирования бизнес-логики настоящая проблема, потому что надо сгенерировать сложнейшие связи в базе данных, которые зависимы друг от друга.

                              Это значит много: во-1, вы скипнули юнит-тесты и пишете уже интеграционный тест. Это вследствие (неиспользования TBC и, как результат) того что объекты у вас тесно связаны, курите dependency invertion. В третьих, (а от этого и ваши 3й и 4й пункты) возможно нарушен принцип single responsibility — объект должен моделью создаваться (скажем одним вызовом addObject()), какой SQL при этом генерируется — его личное инкапсулированное дело. Вы же ведь не достаете данные напрямую из базы, через геттеры ведь достаете (нет?)
                              5. Иметь каждому разработчику отдельную базу и синхронизировать у нас не принято, да и не удобном опять же по причине синхронизации и написания полотен sql скриптов.
                              Ну а это уже совсем напрасно. Один рефакторит/валит базу остальные в это время курят? Разве сложно вынести имя базы в конфиг? Впрочем, я не знаю особенностей вашего приложения.
                                0
                                2DarthSim
                                я пишу для .net. Боюсь что сложность бизнеслогики не позволит использовать автогенераторы данных. граничные и фейковые значение мне не столь сложно проверить, как соответствие дат, денежных сумм, наличия/отсутсвия связей и т.д.

                                2denver
                                простите, но я понял (язык и терминологию не понял) что вы написали в п.2 Прошу вас, объясните подробнее.
                                Объект-то базы создается моделью. Только вот чтобы заполнить все поля объекта (банковское ПО у нас) и найти все связи таблиц (не просто состояния или типы, а до 30 различных логических связей и взаимодействий таблиц) можно потратить несколько дней на такие заготовки для 1 нового класса бизнес-логики по автоматизации чего либо.
                                т.е. не получается просто сделать 2-3 вызова paymentsRepository.AddObject(newPaymentFromCustomer)
                                и тем самым сгенерировать данные.
                                Поэтому честно сказать я не знаю, что с этим делать.

                                to ALL
                                Я правильно понял, что все заливают тестовые данные из файлов перед каждым запуском юнит теста? Ну тогда чем это лучше чем скрипты с запросами к БД.
                                  +1
                                  По феншую, если для юнит-теста нужна база, то это либо тестирование модуля работы с базой (но не модели предметной области приложения), либо это не юнит-тест :)

                                  Объект базы не должен создаваться моделью, по крайней мере захардкодено, объект или класс базы должен задаваться извне класса модели (в конструкторе, в сеттере, в конфиге, где-то ещё), так чтобы можно было перед сохранением модели изменить собственно хранилище (от параметров вроде имени БД/логина до класса этого хранилища, например заменить БД на memcached или вообще на фэйковый объект — mock)
                                  • UFO just landed and posted this here
                                  +1
                                  Про пункты №№2 и 3

                                  1. Mock-objects
                                  2. Yaml/XML/JSON — DB
                                  3. Migrations

                                  Посредством миграций тестовая юаза наполняется идеальными (и нет) данными из файликов. Для проверки кода, использующего внешние зависимости (RPC, SOAP, REST) зависимости перекрываются моками и проверяется, что именно они вызвались.
                                    0
                                    у нас была специальная тестовая база,
                                    создавалась из дампа перед запуском каждого теста
                                    в конце теста — уничтожалась
                                    специальный скрипт для запуска тестов, который запускал тестовую БД, гонял тесты и дропал БД.

                                    недостаток: одновременно нельзя было запустить два теста.
                                    +2
                                    Вот читаю, читаю я статьи на Хабре про TDD и BDD и никак вкурить не могу… Примеры приводятся достаточно элементарные, а вот что-то реально нужного никак не могу прочитать.
                                    Давайте рассмотрим банальный подход по принципу BLL->DAL->DB. Предположим у нас GridView отображает данные возвращаемые одним из методов одного из классов неймспейса BLL. Каким образом писать эти тесты для элементарного получения DataTable из базы и тем что должно получится? Писать сравнение каждой строчки возвращаемой? Или мы интерфейс не тестируем? А что мы тогда тестируем? А как тестировать вставку данных? Опять же для проверки формы из 10 полей (пусть все обязательные) какого же размера тест надо написать для разнообразных комбинаций?
                                      0
                                      тестировать UI — очень непростая задача.
                                      Тесты — это фактически проверка на соответствие требованиям. И если требования к классам легко описываются ассертами, то требования к UI формально описать очень сложно. Отсюда и сложность тестирования.

                                      Работа с базой… модели работы с данными могут быть протестированы с использованием тестовых фикстур данных.
                                        0
                                        То есть тестирование UI слишком сложно — пропускаем… Получается что от TDD плясать уже не получается, как это красиво описано в куче статей, о том как от тестов пишем код…
                                        А от тестовых данных в БД плясать… Ну не знаю. Это получается пиши данные — считывай и проверяй читая все строки и сравнивая все ячейки? Так что ли?
                                        В каких проектах вообще применим TDD? Со сложной логикой? Без сложного UI? Без работы с внешними интерфейсами, включая БД? Потому что иначе размер тестов явно превысит сам код раз в 10.
                                        Это даже взять простейший пример, когда тест пишется для функции имеющий 1 вход, 1 выход: требуется проверить и NULL и "" и маленькую строку и большую и ОЧЕНЬ большую и т.д. А берем пример с запросом к базе данных — а там уже с точки зрения выходных данных пусть даже всего 20 полей и 5 разных комбинаций строк — уже имеем дофига вариантов которые НАДО по логике TDD проверять — а ху-ху не хо-хо — пупок-то не развяжется проверять всякую маловероятную ерунду? Когда же сам код-то писать? Элементарный фильтр из 3 частей в каждой 3 чекбокса уже задолбаешься описывать тестами, да еще и само сравнение с возвращаемыми данными из БД…
                                        А Ajax? А JavaScript с точки зрения того же UI? Боюсь даже представить тест сложносвязанной формы и сколько для этого придется кода писать…

                                        По-моему, надо тщательнее проектировать и аккуратно писать код.
                                          0
                                          Нет никакого грида, аджаска и т.п., есть метода в API получить_данные_такие_то, вот его и надо тестировать в первую очередь, т.е. приложение должно быть спроектировано удобнотестируемым, с выделенным высокоуровневым API. А отрисовку в грид проверит тестер — уверен там если и будут, то очень мелкие ошибки.
                                          Все варианты перебирать не надо, ибо не всегда и возможно, нужно основные, а если хочется побольше перебрать, то можно взять нечто типа LectroTest
                                          В базу ничего писать не надо, нужно использовать Mock объекты
                                            0
                                            Ну вот и как ты будешь «данные_такие_то» проверять? Хоть в DataTable тебе их выдай хоть в чём.
                                            «Все данные проверять не надо» — а как же идея-фикс про 100% покрытие кода тестами? А ВДРУГ мы-то и не догадываемся что тут основное?
                                            В базу писать ничего не надо? А вдруг неправильно запросы написали? Что делать? Неужели руками каждый раз перетестировать?
                                            Короче — что же все-таки дает-то такое тестирование? Тут не тестируй, тут упрости, тут вообще заглушки используй…
                                              0
                                              А вы кажется неправильно TDD представляете. Тест не после кода, а ДО него писать надо. Тогда и покрытие будет 100%, и вопросов не возникнет что тестировать (как что? что хочешь написать, то и проверь сначала).

                                              А в базу писать ничего не надо, надо тестировать только методы класса, их внешнее проявление, а не внутреннюю кухню (потому что сегодня там MySQL, а завтра Mongo). Если запрос там внутри «неправильно» написан, а тесты зеленые, то ничего, как говорится, и не трогай :)
                                                0
                                                Пардон. Если запрос там внутри «неправильно» написан, а тесты зеленые, и все новые тесты (в попытке повалить ожидаемое поведение) выходят также зелеными, то ничего, как говорится, и не трогай :)
                                                0
                                                Так и буду проверять — если при заданных условиях вернулся ожидаемый набор данных, то ок, более конкретно можно сказать для более конкретного примера
                                                100% покрытие это идеал, но мы в реальном мире, тесты очень ценны даже если не покрывают 100%, а покрытие со временем улучшится до достаточного уровня, начать нужно с того что кажется основным
                                                Не надо путать модульные тесты и интеграционные тесты, модульные проверяют только модуль в изолированном окружении, а интеграционные работу модулей вместе, в интеграционных можно и в базу писать тестовые данные
                                                Даёт знание что модуль с высокой вероятностью работает по спецификации, и что особенно важно — знание того, что наше очередное изменение не ухудшило работу модуля
                                                  0
                                                  Тестировать надо прежде всего BLL (бизнес лоджик лэйер — я правильно понял?). DAL (дата абстракшн лэйер?) и DB уже как бы считаются идеальными и в тестировании не нуждаются (их тестировал их разработчик) — вы априори считаете, что если передали DAL запрос на сохранение объекта, то он сохранится, на загрузку — загрузится.

                                                  По идее в BLL у вас запросов быть не должно вообще на этапе разработки, если и появятся, то на этапе оптимизации. Мок объекты это не заглушки, а «проверялки» того, что ваш объект правильно вызывает другие объекты. Допустим у вас есть объект User, который в методе Save вызывает метод Save объекта Storage, передавая ему параметры login и password_hash, которые задаются в конструкторе (хэш считается внутри). В тесте вы создаёте объект User, передав в конструкторе строковые константы логин и пароль. Подставляете вместо Storage объект StorageMock, который конфигурируете так, что он ожидает вызова метода Save c логином и хэшем пароля и вызываете метод Save объекта User. Выполняете тест. Если метод Save объекта StorageMock не будет вызван — тест не пройдёт. Если будет вызван, то параметры не будут равны ожидаемым (например, пароль не захэширован будет) — тест не пройдёт. Он пройдёт только если метод будет вызван с ожидаемыми параметрами, а занчит когда вы вызовите настоящий Storage, эти параметры будут переданы ему.

                                                  Вы подставляете вместо Storage объект StorageMock, который предварительно конфигурир
                                                    0
                                                    «Все данные проверять не надо» — а как же идея-фикс про 100% покрытие кода тестами? А ВДРУГ мы-то и не догадываемся что тут основное?

                                                    Для этого есть регрессивные тесты. Обнаружили баг, поняли что что-то забыли, тогда и создаём тест на этот случай. А изначально описываются лишь основные варианты.
                                                  0
                                                  В каких проектах вообще применим TDD? Со сложной логикой? Без сложного UI? Без работы с внешними интерфейсами, включая БД? Потому что иначе размер тестов явно превысит сам код раз в 10.

                                                  Это если тестировать всё приложение как «черный ящик». Но юнит-тестируют не всё сразу, а по отдельности, равно как и пишут. Комбинаторику (интеграцию) после этого тестировать можно, но необязательно (нужно для контроля качества, а не для кодинга с TDD).

                                                  Ну и да, TDD применим в любых проектах. И чем сложнее проект, тем более необходим. Да, время занимает, но порядка 50%, зато фактически разгружает QA. К тому же необязательно покрывать совсем всё. Можно например только «ядро» и хрупкие участки. Ну а UI покрывать имхо стоит уже в самую последнюю очередь (поскольку меняется чаще всего).
                                              0
                                              А может кто-то написать действительно толковую статью о TDD?
                                              Я вот решил использовать TDD, но пока что никак не могу переделать мозги под написание тестов. Открываю файл для тестов и вступаю в ступор…
                                              С чего начать? Как называть? Что в конце-концов тестить? Ведь можно углубляться до бесконечности в требуемый функционал!
                                                +3
                                                Кент Бек. Экстремальное программирование: разработка через тестирование. — «Питер», 2003

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

                                                Начинать проще всего, имхо, если говорить о вебе и MVC, с модели, тестировать именно бизнес-логику, а не как она сохраняется в БД. Идеально для этого подходит паттерн DataMapper, с ActiveRecord похуже, имхо. Самое сложное по началу выбрать нужный шаг тестирования, когда начинал писать первый проект, используя TDD, то тестировал абсолютно всё, включая среду выполнения, транслятор, сторонние библиотеки (Doctrine в частности :) ).

                                                  0
                                                  Действительно, может, вам лучше хорошую книжку почитать?

                                                  Или может, это поможет habrahabr.ru/blogs/tdd/112851/?
                                                  –1
                                                  Мой рабочий цикл: эксперимент — спецификация — тест — реализация — верификация.

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

                                                  Only users with full accounts can post comments. Log in, please.