0:00:00.000,0:00:04.037 Итак, начнем с этого чрезвычайно практичного подхода к тестированию. 0:00:04.037,0:00:08.064 Тестирование, я должен признаться, где-то всего лишь три или четыре года назад 0:00:08.064,0:00:12.096 я принял как религию. И то, что я имею в виду, отчасти так и было: мне говорили, что это 0:00:12.096,0:00:16.092 важно, я верил, что это важно, я пытался делать это, но именно этот подход действительно изменил 0:00:16.092,0:00:21.035 мою жизнь. И я надеюсь, изменит и вашу тоже. 0:00:21.035,0:00:25.047 И нет, я не о выборах, это ни в коем случае. Итак, давайте поговорим о 0:00:25.047,0:00:29.021 юнит-тестах. В книге есть небольшая вводная о различных видах тестирования. 0:00:29.021,0:00:33.010 Мы сначала заострим внимание на юнит-тестах и немного на функциональных тестах, 0:00:33.010,0:00:36.089 и как и со многими другими темами на этих лекциях, есть удобный акроним, который поможет нам запомнить 0:00:36.089,0:00:40.064 каким должны быть хорошие юнит-тесты. Итак, для начала - они должны быть быстрыми - 0:00:40.064,0:00:44.020 т.е. не должны выполняться слишком долго; они должны быть независимыми. 0:00:44.020,0:00:47.077 Это означает, что не должно иметь значения, какой тест надо запускать первым, какой - вторым 0:00:47.077,0:00:51.042 порядок не важен. Они не должны иметь зависимостей друг от друга. 0:00:51.042,0:00:54.093 Они должны быть воспроизводимыми - т.е. если тест находит ошибку, он должен находить ее каждый раз. 0:00:54.093,0:01:01.047 В некоторых случаях этого легко добиться, в других - довольно непросто. 0:01:01.047,0:01:05.018 Самоконтроль. Вот что означало тестирование не так давно для многих компаний: 0:01:05.018,0:01:08.065 Программное обеспечение перебрасывали через стену в отдел контроля качества, а там сотрудники отдела 0:01:08.065,0:01:12.044 вручную понажимают кнопочки в программе, что-то поделают в ней и - "О, оно работает, оно работает" 0:01:12.044,0:01:16.010 Но мы ведь не хотим больше этого, правда? Тест сам должен знать 0:01:16.010,0:01:19.094 прошел ли он или сломался. И чтобы принять это решение не должно требоваться человеческое вмешательство. 0:01:19.094,0:01:23.059 И, наконец, своевременность. Это означает, что тесты должны быть написаны практически одновременно 0:01:23.059,0:01:27.011 с программным кодом. Если код меняется, тесты также также должны быть изменены. 0:01:27.011,0:01:30.090 А на самом деле, мы собираемся делать это еще агрессивнее. Бы будем писать тесты в первую очередь, еще до того как будет написан код. 0:01:30.090,0:01:34.046 То есть настолько своевременно, насколько вы можете. 0:01:34.046,0:01:38.022 Итак что все это значит? Зачем нам нужны быстрые тесты? 0:01:38.022,0:01:42.022 Затем, что мы можем запускать какое-то подмножество тестов все время. 0:01:42.022,0:01:46.007 И если у нас тысячи и тысячи юнит-тестов, что не является необычным даже для проектов средних размеров, 0:01:46.007,0:01:49.087 это может занять, как вы догадываетесь, минуту или две, чтобы выполнить набор тестов и это будет нас тормозить. 0:01:49.087,0:01:53.043 Все что нам нужно - возможность быстро запускать только те тесты, которые относятся к конкретному куску кода, 0:01:53.043,0:01:57.038 над которым мы работаем таким образом, чтобы не сбиваться с ритма. 0:01:57.038,0:02:01.033 Независимость нужна по той же причине, чтобы мы могли запустить любое подмножество тестов 0:02:01.033,0:02:05.027 и в любом порядке, в каком захотим. 0:02:05.027,0:02:09.011 Поэтому, плохо, когда есть множество тестов, которые можно запускать 0:02:09.011,0:02:13.023 только тогда, когда перед ними запускали какие-либо другие тесты. 0:02:13.023,0:02:17.001 Воспроизводимость, как вы снова догадываетесь, означает что запустив тест N раз мы получим одинаковые результаты. 0:02:17.001,0:02:20.089 Если мы хотим локализовать ошибку и включить возможность автоматической отладки, воспроизводимость важна. 0:02:20.089,0:02:25.007 Самоконтроль: как я уже говорил, человек не должен проверять результат. 0:02:25.007,0:02:29.019 Это означает, что мы можем иметь тесты, выполняющиеся в фоне все время 0:02:29.019,0:02:33.022 и когда бы мы не внесли изменения, которые ломают что-то в 25 милях в другом участке кода, 0:02:33.022,0:02:37.015 какой-нибудь тест обнаружит это и привлечет наше внимание. 0:02:37.015,0:02:40.093 И, наконец, своевременность. Как я и говорил, мы собираемся использовать разработку через тестирование, 0:02:40.093,0:02:45.062 в которой тесты пишутся перед тем, как будет написан программный код. 0:02:45.062,0:02:49.096 Мы будем использовать RSpec, который я рассматриваю как предметно-ориентированный язык для написания тестов. 0:02:49.096,0:02:53.086 Для тех, кто не знаком с подобными языками, скажу, что в основе это вроде небольшого языка программирования 0:02:53.086,0:02:58.008 который умеет делать небольшое количество вещей в рамках одной предметной области. Т.е. это не язык общего назначения. 0:02:58.024,0:03:02.057 На самом деле мы уже видели примеры подобного языка. Миграции являются разновидностью DSL. 0:03:02.057,0:03:06.041 Это небольшое подмножество операторов, чья единственная работа - описывать изменения в схему базы данных.. 0:03:06.041,0:03:10.057 Т.о., миграции оказались DSL, который встроен в Ruby, т.е. 0:03:10.057,0:03:14.074 миграции являются всего лишь кодом на Ruby, но стилизованные под задачи, которые они выполняют. 0:03:14.074,0:03:19.016 На самом деле мы увидим, что RSpec - похожий пример. Итак, мы будем называть все подобное внутренним DSL. 0:03:19.016,0:03:23.047 Он реализован внутри другого языка. Регулярные выражения - тоже внутренний DSL. 0:03:23.047,0:03:27.088 Он как подмножество действий, которые мы можем выполнять в регулярных выражениях. 0:03:27.088,0:03:32.055 Другой пример - внешний или автономный DSL - это SQL. SQL-запросы к базам данных. 0:03:32.055,0:03:36.047 Это отдельный язык, и те, кто работали с другими фреймворками 0:03:36.047,0:03:40.039 перед тем, как перейти к Rails, обычно заканчивали тем, что писали SQL-запросы 0:03:40.039,0:03:44.056 и затем передавали кому-то, да? Так вот это очень яркий пример работы с разными языками. 0:03:44.056,0:03:48.047 Итак, в RSpec, каждый тест называется спекой - от "спецификация" (specification). 0:03:48.047,0:03:52.041 Как ни странно, они размещаются в каталоге, называемом "spec" - потому что нам нравится все делать просто. 0:03:52.041,0:03:56.076 В Rails есть генератор, "rspec:install", который создает структуру подкаталогов. 0:03:56.076,0:04:01.066 Все это есть в книге и в последующих демонстрациях, которые мы покажем сегодня, мы подразумеваем, 0:04:01.066,0:04:05.090 что мы уже выполнили эти подготовительные шаги. 0:04:05.090,0:04:10.086 Итак, с чего все начинается? Подкаталоги каталога spec организованы так, чтобы отражать структуру 0:04:10.086,0:04:14.094 нашего приложения. Так вот: в app/models у вас лежат ваши модели, 0:04:14.094,0:04:19.061 а в spec/models у вас лежат spec-файл для каждой модели. Ничего удивительного. 0:04:19.061,0:04:24.016 Подобным же образом располагаются спеки для контроллеров. А как насчет представлений (view)? 0:04:24.016,0:04:28.060 Вообще, мы не будем делать спеки для представлений. Сделать их можно, но будет это несколько 0:04:28.060,0:04:33.021 кривовато - много из того, что мы хотим проверить в представлении, на самом деле 0:04:33.021,0:04:37.094 можно проверить в контроллере, и мы увидим это в сегодняшнем примере. 0:04:37.094,0:04:42.084 К тому же, мы решили, что наш подход для веб-приложений, с которыми напрямую взаимодействуют пользователи 0:04:42.084,0:04:46.073 - через создание пользовательских историй, которые описывают те части приложения, с которыми взаимодействует заказчик. 0:04:46.073,0:04:50.058 Таким образом, то, что является частью представления - что должно быть видимым в представлении 0:04:50.058,0:04:54.019 и то, что может быть нажато и т.д., мы для всего этого будем использовать Cucumber. 0:04:54.019,0:04:58.009 И в дальнейшем мы так и будем поступать. 0:04:58.023,0:05:02.003 Таким образом в основном мы будем уделять внимание RSpec с точки зрения 0:05:02.003,0:05:05.060 написания спецификаций для наших моделей и контроллеров. 0:05:05.060,0:05:08.095 Итак, давайте начнем с примера новой гипотетической фичи для RottenPotatoes, 0:05:08.095,0:05:13.047 при помощи которой мы можем добавлять фильмы, используя данные из TMDb. 0:05:13.047,0:05:17.098 TMDb - реальный сайт. Он вроде IMDb, но только не коммерческий, что-то вроде open-source проекта. 0:05:17.098,0:05:22.028 Идея в том, что у них есть вся информация о фильмах, и если мы хотим 0:05:22.028,0:05:26.058 добавить фильм в RottenPotatoes, то почему бы нам просто не скачать нужную информацию оттуда? 0:05:26.058,0:05:31.039 На деле, когда мы обсуждали пользовательские истории, в одной из них был шаг, в котором говорится 0:05:31.039,0:05:36.011 "Я заполняю ключевые слова для поиска, Я хочу найти фильм "Начало", 0:05:36.011,0:05:40.059 и когда я нажимаю кнопку, мне предлагают "Искать на TMDb", не так ли? 0:05:40.059,0:05:45.019 Таким образом, предполагается, что должна быть кнопка, по нажатию которой наше приложение обратится к TMDb 0:05:45.019,0:05:49.033 и проверит, есть ли там "Начало", и если да, возьмет информацию о нем оттуда. 0:05:49.033,0:05:53.020 Вопрос в том, что мы должны сделать. Какой код нужно написать 0:05:53.020,0:05:57.003 и какой вид тестирования нужно применить. 0:05:57.003,0:06:01.016 И прежде, чем мы займемся этим, помните наш разговор о "Кулинарии Rails", 0:06:01.016,0:06:05.019 рецептах, как готовить при помощи Rails? Вспомните, что когда мы добавляем какую-либо новую фичу 0:06:05.019,0:06:08.088 это означает, что нам нужен новый маршрут (route), новый метод контроллера. Также нам может понадобиться, 0:06:08.088,0:06:13.001 а может и нет, новое представление. Это зависит от того, может ли эта фича использовать существующее представление 0:06:13.001,0:06:17.000 или мы должны сделать новое. Эти шаги мы должны выполнять всегда. 0:06:17.000,0:06:20.095 Итак, давайте займемся всем этим, но будем делать по одному шагу за раз. 0:06:20.095,0:06:25.012 Ладно, так. Это идея, которую мы будем видеть многократно. Я всего лишь приучаю вас к фразе 0:06:25.012,0:06:29.060 "Код, который вы бы хотели иметь." Это очень сильная мысль, как только вы к ней привыкнете. 0:06:29.060,0:06:33.098 Она странно звучит, когда слышите ее первый раз, но она действительно очень сильная. 0:06:33.098,0:06:38.028 Итак, мы спрашиваем себя: "Хорошо, когда пользователь нажимает на эту кнопку "Искать на TMDb", мы знаем, что 0:06:38.028,0:06:42.004 где-то должен быть метод контроллера, который получит все то, что было отправлено через форму. 0:06:42.004,0:06:45.076 Так что должен делать этот метод? Что должен делать метод контроллера, когда получает форму поиска? 0:06:45.076,0:06:49.030 Если бы нас спросили об этом, и если бы мы написали, что он должен делать на разговорном языке 0:06:49.030,0:06:52.061 наш ответ звучал бы как "Ладно, смотри, он должен вызывать метод (который мы еще не написали) 0:06:52.061,0:06:56.033 который сходит на TMDb и поищет там фильм". Разумно. 0:06:56.033,0:07:00.009 "Если он там найдет фильм, он должен сформировать некоторое представление, чтобы показать результат поиска 0:07:00.023,0:07:03.094 (и снова, мы пока еще не создали это представление, но логически, это как раз то, что мы собираемся 0:07:03.094,0:07:07.039 написать)." Сегодня на третий шаг нам времени не хватит, но для неуспешного исхода сценарий таков: 0:07:07.039,0:07:11.048 "Если фильм не найден, метод должен перенаправить на главную страницу RottenPotatoes и сообщить 0:07:11.048,0:07:15.086 "Ничего не найдено". И если если вы просмотрите пример в книге, 0:07:15.086,0:07:20.003 мы на самом деле вариант неуспешного исхода проработали в главе, посвященной BDD. 0:07:20.003,0:07:24.014 Итак, у нас есть эти два момента. Сосредоточимся на №1 и №2 - это то, что метод контроллера должен делать, 0:07:24.014,0:07:28.025 и вот как мы будем это описывать. Где моя мышь? Итак, поехали. 0:07:28.025,0:07:32.075 Рассмотрим, как мы опишем все эти требования при помощи RSpec. Итак... 0:07:32.075,0:07:38.096 Ничего особенного здесь ни происходит, видите. Все достаточно просто, не так ли? 0:07:38.096,0:07:43.081 И это корректный код RSpec. SpecHelper - это просто файл, который RSpec создает как часть шага установки. 0:07:43.081,0:07:48.083 Он просто выполняет кое-какие вещи, гарантирующие, что все необходимое подгружено. 0:07:48.083,0:07:52.089 И собираемся начать, спросив "Что должен проверять этот тест?" 0:07:52.089,0:07:57.037 Или, мы собираемся описать поведение MoviesController. 0:07:57.037,0:08:01.059 У контроллера есть много вариантов поведения, но нас волнует одно конкретное, а именно 0:08:01.059,0:08:05.065 поведение во время поиска на TMDb. Итак, мы можем представить, для каждого поведения в контроллере 0:08:05.065,0:08:09.050 как растет наша спека, мы будем добавлять больше описательных блоков внутри этого внешнего 0:08:09.050,0:08:13.049 "describe MoviesController", и так далее вкладывать, вкладывать, вкладывать. 0:08:13.049,0:08:17.009 Все, что я здесь сделал - это переписал те три требования, о которых мы говорили. 0:08:17.009,0:08:21.093 Итак, "it" - это на самом деле вызов метода в RSpec. Он принимает аргумент, который является 0:08:21.093,0:08:25.085 строкой, описывающий, что должно произойти. И как мы увидим, 0:08:25.085,0:08:29.079 он принимает также второй аргумент, который является процедурой, которая и выполняет тест. 0:08:29.079,0:08:33.065 Но пока, все что мы сделали - лишь сделали перевод. Мы обдумали три требования 0:08:33.065,0:08:37.004 которые должен удовлетворять контроллер, и мы написали эти три требования в RSpec. 0:08:37.004,0:08:41.000 Этого достаточно, чтобы выполнить запуск, и у нас есть скринкаст, который я рекомендую вам посмотреть. 0:08:41.000,0:08:44.086 Он соответствует главе книги и в нем как раз все это делается. 0:08:45.000,0:08:48.086 Все, что он делает - запускает три теста, которые ничего не делают. Поэтом вывод RSpec желтый, да? 0:08:48.086,0:08:52.058 Желтый означает "еще не реализовано", точно как и в Cucumber.