Return to Video

5.2 - Принцип FIRST, TDD и начало работы с RSpec

  • 0:00 - 0:04
    Итак, начнем с этого чрезвычайно практичного подхода к тестированию.
  • 0:04 - 0:08
    Тестирование, я должен признаться, где-то всего лишь три или четыре года назад
  • 0:08 - 0:12
    я принял как религию. И то, что я имею в виду, отчасти так и было: мне говорили, что это
  • 0:12 - 0:16
    важно, я верил, что это важно, я пытался делать это, но именно этот подход действительно изменил
  • 0:16 - 0:21
    мою жизнь. И я надеюсь, изменит и вашу тоже.
  • 0:21 - 0:25
    И нет, я не о выборах, это ни в коем случае. Итак, давайте поговорим о
  • 0:25 - 0:29
    юнит-тестах. В книге есть небольшая вводная о различных видах тестирования.
  • 0:29 - 0:33
    Мы сначала заострим внимание на юнит-тестах и немного на функциональных тестах,
  • 0:33 - 0:36
    и как и со многими другими темами на этих лекциях, есть удобный акроним, который поможет нам запомнить
  • 0:36 - 0:40
    каким должны быть хорошие юнит-тесты. Итак, для начала - они должны быть быстрыми -
  • 0:40 - 0:44
    т.е. не должны выполняться слишком долго; они должны быть независимыми.
  • 0:44 - 0:47
    Это означает, что не должно иметь значения, какой тест надо запускать первым, какой - вторым
  • 0:47 - 0:51
    порядок не важен. Они не должны иметь зависимостей друг от друга.
  • 0:51 - 0:54
    Они должны быть воспроизводимыми - т.е. если тест находит ошибку, он должен находить ее каждый раз.
  • 0:54 - 1:01
    В некоторых случаях этого легко добиться, в других - довольно непросто.
  • 1:01 - 1:05
    Самоконтроль. Вот что означало тестирование не так давно для многих компаний:
  • 1:05 - 1:08
    Программное обеспечение перебрасывали через стену в отдел контроля качества, а там сотрудники отдела
  • 1:08 - 1:12
    вручную понажимают кнопочки в программе, что-то поделают в ней и - "О, оно работает, оно работает"
  • 1:12 - 1:16
    Но мы ведь не хотим больше этого, правда? Тест сам должен знать
  • 1:16 - 1:19
    прошел ли он или сломался. И чтобы принять это решение не должно требоваться человеческое вмешательство.
  • 1:19 - 1:23
    И, наконец, своевременность. Это означает, что тесты должны быть написаны практически одновременно
  • 1:23 - 1:27
    с программным кодом. Если код меняется, тесты также также должны быть изменены.
  • 1:27 - 1:30
    А на самом деле, мы собираемся делать это еще агрессивнее. Бы будем писать тесты в первую очередь, еще до того как будет написан код.
  • 1:30 - 1:34
    То есть настолько своевременно, насколько вы можете.
  • 1:34 - 1:38
    Итак что все это значит? Зачем нам нужны быстрые тесты?
  • 1:38 - 1:42
    Затем, что мы можем запускать какое-то подмножество тестов все время.
  • 1:42 - 1:46
    И если у нас тысячи и тысячи юнит-тестов, что не является необычным даже для проектов средних размеров,
  • 1:46 - 1:49
    это может занять, как вы догадываетесь, минуту или две, чтобы выполнить набор тестов и это будет нас тормозить.
  • 1:49 - 1:53
    Все что нам нужно - возможность быстро запускать только те тесты, которые относятся к конкретному куску кода,
  • 1:53 - 1:57
    над которым мы работаем таким образом, чтобы не сбиваться с ритма.
  • 1:57 - 2:01
    Независимость нужна по той же причине, чтобы мы могли запустить любое подмножество тестов
  • 2:01 - 2:05
    и в любом порядке, в каком захотим.
  • 2:05 - 2:09
    Поэтому, плохо, когда есть множество тестов, которые можно запускать
  • 2:09 - 2:13
    только тогда, когда перед ними запускали какие-либо другие тесты.
  • 2:13 - 2:17
    Воспроизводимость, как вы снова догадываетесь, означает что запустив тест N раз мы получим одинаковые результаты.
  • 2:17 - 2:20
    Если мы хотим локализовать ошибку и включить возможность автоматической отладки, воспроизводимость важна.
  • 2:20 - 2:25
    Самоконтроль: как я уже говорил, человек не должен проверять результат.
  • 2:25 - 2:29
    Это означает, что мы можем иметь тесты, выполняющиеся в фоне все время
  • 2:29 - 2:33
    и когда бы мы не внесли изменения, которые ломают что-то в 25 милях в другом участке кода,
  • 2:33 - 2:37
    какой-нибудь тест обнаружит это и привлечет наше внимание.
  • 2:37 - 2:40
    И, наконец, своевременность. Как я и говорил, мы собираемся использовать разработку через тестирование,
  • 2:40 - 2:45
    в которой тесты пишутся перед тем, как будет написан программный код.
  • 2:45 - 2:49
    Мы будем использовать RSpec, который я рассматриваю как предметно-ориентированный язык для написания тестов.
  • 2:49 - 2:53
    Для тех, кто не знаком с подобными языками, скажу, что в основе это вроде небольшого языка программирования
  • 2:53 - 2:58
    который умеет делать небольшое количество вещей в рамках одной предметной области. Т.е. это не язык общего назначения.
  • 2:58 - 3:02
    На самом деле мы уже видели примеры подобного языка. Миграции являются разновидностью DSL.
  • 3:02 - 3:06
    Это небольшое подмножество операторов, чья единственная работа - описывать изменения в схему базы данных..
  • 3:06 - 3:10
    Т.о., миграции оказались DSL, который встроен в Ruby, т.е.
  • 3:10 - 3:14
    миграции являются всего лишь кодом на Ruby, но стилизованные под задачи, которые они выполняют.
  • 3:14 - 3:19
    На самом деле мы увидим, что RSpec - похожий пример. Итак, мы будем называть все подобное внутренним DSL.
  • 3:19 - 3:23
    Он реализован внутри другого языка. Регулярные выражения - тоже внутренний DSL.
  • 3:23 - 3:27
    Он как подмножество действий, которые мы можем выполнять в регулярных выражениях.
  • 3:27 - 3:32
    Другой пример - внешний или автономный DSL - это SQL. SQL-запросы к базам данных.
  • 3:32 - 3:36
    Это отдельный язык, и те, кто работали с другими фреймворками
  • 3:36 - 3:40
    перед тем, как перейти к Rails, обычно заканчивали тем, что писали SQL-запросы
  • 3:40 - 3:44
    и затем передавали кому-то, да? Так вот это очень яркий пример работы с разными языками.
  • 3:44 - 3:48
    Итак, в RSpec, каждый тест называется спекой - от "спецификация" (specification).
  • 3:48 - 3:52
    Как ни странно, они размещаются в каталоге, называемом "spec" - потому что нам нравится все делать просто.
  • 3:52 - 3:56
    В Rails есть генератор, "rspec:install", который создает структуру подкаталогов.
  • 3:56 - 4:01
    Все это есть в книге и в последующих демонстрациях, которые мы покажем сегодня, мы подразумеваем,
  • 4:01 - 4:05
    что мы уже выполнили эти подготовительные шаги.
  • 4:05 - 4:10
    Итак, с чего все начинается? Подкаталоги каталога spec организованы так, чтобы отражать структуру
  • 4:10 - 4:14
    нашего приложения. Так вот: в app/models у вас лежат ваши модели,
  • 4:14 - 4:19
    а в spec/models у вас лежат spec-файл для каждой модели. Ничего удивительного.
  • 4:19 - 4:24
    Подобным же образом располагаются спеки для контроллеров. А как насчет представлений (view)?
  • 4:24 - 4:28
    Вообще, мы не будем делать спеки для представлений. Сделать их можно, но будет это несколько
  • 4:28 - 4:33
    кривовато - много из того, что мы хотим проверить в представлении, на самом деле
  • 4:33 - 4:37
    можно проверить в контроллере, и мы увидим это в сегодняшнем примере.
  • 4:37 - 4:42
    К тому же, мы решили, что наш подход для веб-приложений, с которыми напрямую взаимодействуют пользователи
  • 4:42 - 4:46
    - через создание пользовательских историй, которые описывают те части приложения, с которыми взаимодействует заказчик.
  • 4:46 - 4:50
    Таким образом, то, что является частью представления - что должно быть видимым в представлении
  • 4:50 - 4:54
    и то, что может быть нажато и т.д., мы для всего этого будем использовать Cucumber.
  • 4:54 - 4:58
    И в дальнейшем мы так и будем поступать.
  • 4:58 - 5:02
    Таким образом в основном мы будем уделять внимание RSpec с точки зрения
  • 5:02 - 5:05
    написания спецификаций для наших моделей и контроллеров.
  • 5:05 - 5:08
    Итак, давайте начнем с примера новой гипотетической фичи для RottenPotatoes,
  • 5:08 - 5:13
    при помощи которой мы можем добавлять фильмы, используя данные из TMDb.
  • 5:13 - 5:17
    TMDb - реальный сайт. Он вроде IMDb, но только не коммерческий, что-то вроде open-source проекта.
  • 5:17 - 5:22
    Идея в том, что у них есть вся информация о фильмах, и если мы хотим
  • 5:22 - 5:26
    добавить фильм в RottenPotatoes, то почему бы нам просто не скачать нужную информацию оттуда?
  • 5:26 - 5:31
    На деле, когда мы обсуждали пользовательские истории, в одной из них был шаг, в котором говорится
  • 5:31 - 5:36
    "Я заполняю ключевые слова для поиска, Я хочу найти фильм "Начало",
  • 5:36 - 5:40
    и когда я нажимаю кнопку, мне предлагают "Искать на TMDb", не так ли?
  • 5:40 - 5:45
    Таким образом, предполагается, что должна быть кнопка, по нажатию которой наше приложение обратится к TMDb
  • 5:45 - 5:49
    и проверит, есть ли там "Начало", и если да, возьмет информацию о нем оттуда.
  • 5:49 - 5:53
    Вопрос в том, что мы должны сделать. Какой код нужно написать
  • 5:53 - 5:57
    и какой вид тестирования нужно применить.
  • 5:57 - 6:01
    И прежде, чем мы займемся этим, помните наш разговор о "Кулинарии Rails",
  • 6:01 - 6:05
    рецептах, как готовить при помощи Rails? Вспомните, что когда мы добавляем какую-либо новую фичу
  • 6:05 - 6:08
    это означает, что нам нужен новый маршрут (route), новый метод контроллера. Также нам может понадобиться,
  • 6:08 - 6:13
    а может и нет, новое представление. Это зависит от того, может ли эта фича использовать существующее представление
  • 6:13 - 6:17
    или мы должны сделать новое. Эти шаги мы должны выполнять всегда.
  • 6:17 - 6:20
    Итак, давайте займемся всем этим, но будем делать по одному шагу за раз.
  • 6:20 - 6:25
    Ладно, так. Это идея, которую мы будем видеть многократно. Я всего лишь приучаю вас к фразе
  • 6:25 - 6:29
    "Код, который вы бы хотели иметь." Это очень сильная мысль, как только вы к ней привыкнете.
  • 6:29 - 6:33
    Она странно звучит, когда слышите ее первый раз, но она действительно очень сильная.
  • 6:33 - 6:38
    Итак, мы спрашиваем себя: "Хорошо, когда пользователь нажимает на эту кнопку "Искать на TMDb", мы знаем, что
  • 6:38 - 6:42
    где-то должен быть метод контроллера, который получит все то, что было отправлено через форму.
  • 6:42 - 6:45
    Так что должен делать этот метод? Что должен делать метод контроллера, когда получает форму поиска?
  • 6:45 - 6:49
    Если бы нас спросили об этом, и если бы мы написали, что он должен делать на разговорном языке
  • 6:49 - 6:52
    наш ответ звучал бы как "Ладно, смотри, он должен вызывать метод (который мы еще не написали)
  • 6:52 - 6:56
    который сходит на TMDb и поищет там фильм". Разумно.
  • 6:56 - 7:00
    "Если он там найдет фильм, он должен сформировать некоторое представление, чтобы показать результат поиска
  • 7:00 - 7:03
    (и снова, мы пока еще не создали это представление, но логически, это как раз то, что мы собираемся
  • 7:03 - 7:07
    написать)." Сегодня на третий шаг нам времени не хватит, но для неуспешного исхода сценарий таков:
  • 7:07 - 7:11
    "Если фильм не найден, метод должен перенаправить на главную страницу RottenPotatoes и сообщить
  • 7:11 - 7:15
    "Ничего не найдено". И если если вы просмотрите пример в книге,
  • 7:15 - 7:20
    мы на самом деле вариант неуспешного исхода проработали в главе, посвященной BDD.
  • 7:20 - 7:24
    Итак, у нас есть эти два момента. Сосредоточимся на №1 и №2 - это то, что метод контроллера должен делать,
  • 7:24 - 7:28
    и вот как мы будем это описывать. Где моя мышь? Итак, поехали.
  • 7:28 - 7:32
    Рассмотрим, как мы опишем все эти требования при помощи RSpec. Итак...
  • 7:32 - 7:38
    Ничего особенного здесь ни происходит, видите. Все достаточно просто, не так ли?
  • 7:38 - 7:43
    И это корректный код RSpec. SpecHelper - это просто файл, который RSpec создает как часть шага установки.
  • 7:43 - 7:48
    Он просто выполняет кое-какие вещи, гарантирующие, что все необходимое подгружено.
  • 7:48 - 7:52
    И собираемся начать, спросив "Что должен проверять этот тест?"
  • 7:52 - 7:57
    Или, мы собираемся описать поведение MoviesController.
  • 7:57 - 8:01
    У контроллера есть много вариантов поведения, но нас волнует одно конкретное, а именно
  • 8:01 - 8:05
    поведение во время поиска на TMDb. Итак, мы можем представить, для каждого поведения в контроллере
  • 8:05 - 8:09
    как растет наша спека, мы будем добавлять больше описательных блоков внутри этого внешнего
  • 8:09 - 8:13
    "describe MoviesController", и так далее вкладывать, вкладывать, вкладывать.
  • 8:13 - 8:17
    Все, что я здесь сделал - это переписал те три требования, о которых мы говорили.
  • 8:17 - 8:21
    Итак, "it" - это на самом деле вызов метода в RSpec. Он принимает аргумент, который является
  • 8:21 - 8:25
    строкой, описывающий, что должно произойти. И как мы увидим,
  • 8:25 - 8:29
    он принимает также второй аргумент, который является процедурой, которая и выполняет тест.
  • 8:29 - 8:33
    Но пока, все что мы сделали - лишь сделали перевод. Мы обдумали три требования
  • 8:33 - 8:37
    которые должен удовлетворять контроллер, и мы написали эти три требования в RSpec.
  • 8:37 - 8:41
    Этого достаточно, чтобы выполнить запуск, и у нас есть скринкаст, который я рекомендую вам посмотреть.
  • 8:41 - 8:44
    Он соответствует главе книги и в нем как раз все это делается.
  • 8:45 - 8:48
    Все, что он делает - запускает три теста, которые ничего не делают. Поэтом вывод RSpec желтый, да?
  • 8:48 - 8:52
    Желтый означает "еще не реализовано", точно как и в Cucumber.
Title:
5.2 - Принцип FIRST, TDD и начало работы с RSpec
Description:

В этом видео Армандо рассказывает о пяти свойствах хороших юнит-тестов, описывает подход к разработке через тестирование (TDD) и начинает демонстрацию RSpec.

more » « less
Video Language:
English

Russian subtitles

Revisions