Глава 8. Проверка в среде Docker

В своей предыдущей главе мы завершили установку своего стандартного приложения добавив базу данных Postgres. Однако мы ещё не всё сделали для своей разработки. Будучи профессиональными разработчиками Ruby мы ценим хорошо протестированный код, который предоставляет нам уверенность в том, что мы поставляем надёжное программное обеспечение. Когда мы перестраиваем свою среду разработки на Docker, нам требуется выяснить как проверка вписывается в общую картину. Какими бы ни были ваши личные предпочтения относительно проверок, важно знать как заставить наши инструменты тестирования работать и хорошо воспроизводиться в Docker с тем, чтобы вы могли применять их в случае их необходимости.

В этой главе мы намерены настроить популярную инфраструктуру проверок Ruby: RSpec. Я выбрал RSpec вместо среды Rails по умолчанию - Minitest - по паре причин. Во- первых, для настройки RSpec требуется немного больше усилий, поэтому предстоит ещё многое узнать. Во- вторых, так получилось, что именно это является моим выбором инфраструктуры проверок проектов Rails.

Тем не менее, если вы страстный поклонник Minitest, нечего беспокоиться. Вам всё равно стоит прочесть эту главу чтобы продолжить знакомство с командами Docker, требующимися в нашем обычном рабочем процессе. Кроме того, требуемые настройки, в особенности для Capybara, будут во многом теми же самыми и могут быть в значительной степени перенесены в Minitest.

Если вы когда либо настраивали RSpec в проекте Rails, большая часть того что вы обнаружите в этой главе будет вам очень знакомо. На самом деле бегло просмотрев, вы вообще можете пропустить тот факт что мы применяем Docker. Это свидетельствует как о том, насколько далеко вы продвинулись в изучении и понимании Docker, так и о том, что после своей настройки инструменты Docker исчезают с вашего пути и уходят на задний план пока они нам не потребуются.

Несмотря на это, нельзя сказать что данная глава не сталкивается с проблемами. Имеется несколько нюансов для проверок при помощи Docker, которые мы и отобразим. Мы также обнаружим что не всё так просто когда мы приступим к настройке системных спецификаций при помощи Capybara для сквозной проверки браузером.

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

И вновь в пучину...

Настройка RSpec

Теперь, когда наше приложение правильно настроено под Compose, настройка RSpec покажется нам очень знакомой. Давайте быстро пронесёмся по ней.

Следуя инструкциям rspec-rails нам требуется добавить в свой Gemfile следующее:


​ 	group ​:development​, ​:test​ ​do​
​ 	  ​# Call 'byebug' anywhere in the code to stop execution and get a debugger…​
​ 	  gem ​'byebug'​, ​platforms: ​[​:mri​, ​:mingw​, ​:x64_mingw​]
»	  gem ​'rspec-rails'​, ​'~> 3.8'​
​ 	​end​
 	   

Прежде всего давайте остановим свой сервер web:


​ 	​$ ​​docker-compose​​ ​​stop​​ ​​web​
		

Далее нам требуется заново собрать свой образ чтобы запустить bundle install и затем создать из него некий новый контейнер:


​ 	​$ ​​docker-compose​​ ​​build​​ ​​web​
​ 	Building web
​ 	Step 1/12 : FROM ruby:2.6
​ 	...
​ 	Step 7/12 : RUN apt-get update -yqq && apt-get install -yqq --no-install-
​ 	recommends   nodejs   yarn
​ 	...
​ 	Bundle complete! 18 Gemfile dependencies, 77 gems now installed.
​ 	Bundled gems are installed into `/usr/local/bundle`
​ 	...
​ 	Removing intermediate container dcb3ac9ef4e5
​ 	​ --->​​ ​​a1bf00e74754​
​ 	Step 11/12 : COPY . /usr/src/app/
​ 	​ --->​​ ​​395cd4848b46​
​ 	Step 12/12 : CMD ["bin/rails", "s", "-b", "0.0.0.0"]
​ 	​ --->​​ ​​Running​​ ​​in​​ ​​47ec46df6236​
​ 	Removing intermediate container 47ec46df6236
​ 	​ --->​​ ​​ea5d358cb673​
​ 	Successfully built ea5d358cb673
​ 	Successfully tagged myapp_web:latest
​ 	​$ ​​docker-compose​​ ​​up​​ ​​-d​​ ​​--force-recreate​​ ​​web​
​ 	Recreating myapp_web_1 ... done
		

Далее нам требуется установить RSpec, настроив его файловую структуру:


​ 	​$ ​​docker-compose​​ ​​exec​​ ​​web​​ ​​bin/rails​​ ​​generate​​ ​​rspec:install​
​ 	      create  .rspec
​ 	      create  spec
​ 	      create  spec/spec_helper.rb
​ 	      create  spec/rails_helper.rb
		

при настроенном RSpec мы способны запустить спецификацию подобно следующей:


​ 	​$ ​​docker-compose​​ ​​exec​​ ​​web​​ ​​bin/rails​​ ​​spec​
		

Однако, как мы и ожидали, это выдаст нам отчёт о том, что в данный момент у нас отсутствуют спецификации:


​ 	No examples found.
​ 	
​ 	
​ 	Finished in 0.00509 seconds (files took 0.30574 seconds to load)
​ 	0 examples, 0 failures
		

Давайте возьмёмся за RSpec для верного тес- драйва, создав ...

Наша первая проверка

Имея в своём проекте установленный RSpec нам вовсе не улыбается наблюдать ноль исполняемых проверок. Давайте исправим это прямо сейчас создав свой первый тест с тем чтобы мы смогли обнаружить некое реальное исполнение проверки своего кода.

Давайте выработаем некую спецификацию для своей модели User:


​ 	​$ ​​docker-compose​​ ​​exec​​ ​​web​​ ​​bin/rails​​ ​​generate​​ ​​rspec:model​​ ​​user​
​ 	      create  spec/models/user_spec.rb 
		
[Замечание]Пользователи Linux: владение файлами

Убедитесь что вы выполнили chown для данных файлов запустив:


​ 	​$ ​​sudo​​ ​​chown​​ ​​<your_user>:<your_group> ​​-R​​ ​​.​
		

За дополнительными подробностями отсылаем к Дополнению A. Владельцы файлов и полномочия.

Откройте вновь созданный файл spec/models/user_spec.rb в своём редакторе. Это не книга о тестировании - нам всего лишь требуется некая базовая проверка для отображения того что RSpec работает как мы ожидаем. Приводимое ниже должно выполнить необходимый трюк:


​ 	require ​'rails_helper'​
​ 	
​ 	RSpec.​describe​ User ​do​
​ 	  describe ​"validations"​ ​do​
​ 	    it ​"requires first_name to be set"​ ​do​
​ 	      expect(subject.​valid?​).​to_not​ be
​ 	      expect(subject.​errors​[​:first_name​].​size​).​to​ eq(1)
​ 	    ​end​
​ 	
​ 	    it ​"requires last_name to be set"​ ​do​
​ 	      expect(subject.​valid?​).​to_not​ be
​ 	      expect(subject.​errors​[​:last_name​].​size​).​to​ eq(1)
​ 	    ​end​
​ 	  ​end​
​ 	​end​
 	   

Теперь, когда мы запустим свои проверки вновь:


​ 	​$ ​​docker-compose​​ ​​exec​​ ​​web​​ ​​bin/rails​​ ​​spec​
		

мы обнаружим закономерный отказ, так как мы не реализовали никаких аттестаций своей модели User:


​ 	Failures:
​ 	
​ 	 1) User validations requires first_name to be set
​ 	    Failure/Error: expect(subject.valid?).to_not be
​ 	      expected true to evaluate to false
​ 	​    # ./spec/models/user_spec.rb:6:in `block (3 levels) in <top (required)>'​
​ 	
​ 	 2) User validations requires last_name to be set
​ 	    Failure/Error: expect(subject.valid?).to_not be
​ 	      expected true to evaluate to false
​ 	​    # ./spec/models/user_spec.rb:11:in `block (3 levels) in <top (required)>'​
​ 	
​ 	Finished in 0.09403 seconds (files took 17.39 seconds to load)
​ 	2 examples, 2 failures
		

Давайте заставим исполниться проход этих проверок обновив свою модель User (app/models/user.rb) с тем, чтобы она выглядела так:


​ 	​class​ User < ApplicationRecord
​ 	  validates_presence_of ​:first_name​, ​:last_name​
​ 	​end​
 	   

Теперь, когда мы повторно запустим свои спецификации:


​ 	​$ ​​docker-compose​​ ​​exec​​ ​​web​​ ​​bin/rails​​ ​​spec​
		

мы сможем обнаружить что они пройдены:


​ 	..
​ 	
​ 	Finished in 0.07523 seconds (files took 4.69 seconds to load)
​ 	2 examples, 0 failures
		

И это было всё что нам нужно было сделать. После настройки RSpec в Docker, ключевое отличие состоит в том, что нам требуется добавлять в свои команды префикс при помощи docker-compose exec web - надеюсь вы приспособитесь к этому.

Установка системы проверок Rails

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

Хотя некий вид повсеместной проверки был возможен и ранее - например, при помощи спецификаций свойств RSpec - системные проверки привнесли множество преимуществ. нам более не требуется беспокоиться об очистке своей базы данных на протяжении проверок, что обычно выполнялось соответствующим gem Database Cleaner; вместо этого системные проверки запускают код драйвера вашего браузера в том же самом процессе, в котором исполняется Rails, что позволяет этим тестам исполняться в откатываемых обратно транзакциях.

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

Системные спецификации в своих функциях основаны на gem Capybara - который предоставляет некий приличный DSL (domain-specific language, специфичный для области язык) для взаимодействия с выбранным браузером. Следуя инструкциям Capybara, первый шаг состоит в установке необходимого gem.

Давайте теперь добавим его в свой Gemfile:


​ 	group ​:development​, ​:test​ ​do​
​ 	  ​# Call 'byebug' anywhere in the code to stop execution and get a debugger…​
​ 	  gem ​'byebug'​, ​platforms: ​[​:mri​, ​:mingw​, ​:x64_mingw​]
​ 	  gem ​'rspec-rails'​, ​'~> 3.8'​
»	  gem ​'capybara'​, ​'~> 3.7'​
​ 	​end​
 	   

Затем нам требуется установить этот новый gem собрав заново свой образ. Одновременно с этим мы воссоздадим соответствующий контейнер web:


​ 	​$ ​​docker-compose​​ ​​build​​ ​​web​
​ 	​$ ​​docker-compose​​ ​​stop​​ ​​web​
​ 	​$ ​​docker-compose​​ ​​up​​ ​​-d​​ ​​--force-recreate​​ ​​web​
		

Теперь мы готовы к созданию своей самой первой спецификации системы. По умолчанию RSpec ожидает обнаруживать их в своём каталоге spec/system, следовательно давайте создадим его:


​ 	​$ ​​mkdir​​ ​​spec/system​
		

Давайте начнём с создания необходимого файла spec/system/page_views_spec.rb и отредактируем его следующим образом:


​ 	require ​'rails_helper'​
​ 	
​ 	RSpec.​describe​ ​"PageViews"​ ​do​
​ 	  it ​"shows the number of page views"​ ​do​
​ 	    visit ​'/welcome'​
​ 	    expect(page.​text​).​to​ match(​/This page has been viewed [0-9]+ times?!/​)
​ 	  ​end​
​ 	​end​
 	   
[Замечание]Пользователи Linux: владение файлами

Убедитесь что вы выполнили chown для данных файлов запустив:


​ 	​$ ​​sudo​​ ​​chown​​ ​​<your_user>:<your_group> ​​-R​​ ​​.​
		

За дополнительными подробностями отсылаем к Дополнению A. Владельцы файлов и полномочия.

Прежде чем мы запустим эту проверку, давайте переключимся на использование для стандартных системных проверок на имеющийся драйвер RackTest. Не только потому что он быстрее, но также и из- за того что он убережёт нас от повинности в установке полного драйвера с поддержкой JavaScript (по умолчанию это Selenium), пока он на самом деле не потребуется нам.

Измените spec/rails_helper.rb, добавив следующие строки перед самым последним end:


​ 	config.​before​(​:each​, ​type: :system​) ​do​
​ 	  driven_by ​:rack_test​
​ 	​end​
 	   

Это использует точку входа настроек RSpec before для выполнения некоторой настройки перед каждым запуском системной спецификации; в частности, мы применяем метод driven_by - новый метод Rails, предоставляемый для системных проверок - чтобы установить соответствующий драйвер Capybara на rack_test.

Выполнив это, давайте проведём проверки:


​ 	​$ ​​docker-compose​​ ​​exec​​ ​​web​​ ​​rspec​​ ​​spec/system/​
​ 	.
​ 	
​ 	Finished in 27.2 seconds (files took 11.57 seconds to load)
​ 	1 example, 0 failures
		

Великолепно! Мы настроили Capybara и свои системные спецификации.

Выполнение проверок, основанных на JavaScript

Хорошо, давайте ступим на следующую ступень. Настройка проверок с поддержкой JavaScript не будет настолько простой в случае с Docker. Но я полагаю что вы готовы справиться и с этим.

Допустим, если вы пожелаете, что у вас имеется некая расширенная версия нашей страницы /welcome, которая имеет расширения своего поведения, которые работают исключительно с включённым JavaScript. На практике, при верной работе этот JavaScript дословно добавляет на данной странице сообщение "ENHANCED!"

Вот моя довольно грубая реализация в app/views/welcome/index.html.erb:


​ 	​<%​ content_for ​:head​ ​do​ ​%>​
​ 	  <script type=​"text/javascript"​>
​ 	    document.addEventListener(​"DOMContentLoaded"​,​function​(){
​ 	      document.getElementsByTagName(​'h1'​)[0].append(​' ENHANCED!'​);
​ 	    });
​ 	  </script>
​ 	​<%​ ​end​ ​%>​
​ 	
​ 	<h1>This page has been viewed ​<%=​ pluralize(@page_hits, ​'time'​) ​%>​!</h1>
 	   

Нам также требуется подстроить app/views/layouts/application.html.erb чтобы заставить всё это работать:


​​ 	​<!DOCTYPE html>​
​ 	<html>
​ 	  <head>
​ 	    <title>Myapp</title>
​ 	    ​<%=​ csrf_meta_tags ​%>​
​ 	
​ 	    ​<%=​ stylesheet_link_tag ​'application'​,
​ 	                             ​media: ​​'all'​,
​ 	                            ​'data-turbolinks-track'​: ​'reload'​ ​%>​
​ 	
​ 	    ​<%=​ javascript_include_tag ​'application'​,
​ 	                               ​'data-turbolinks-track'​: ​'reload'​ ​%>​
»	​    <%= yield :head %>​
​ 	​  </head>​
​ 	
​ 	​  <body>​
​ 	​    <%= yield %>​
​ 	  </body>
​ 	</html>
 	   

Давайте добавим некий второй scenario в свою системную спецификацию PageViews для проверки этого поведения (запомните, наша цель состоит в данном случае в демонстрации того как настроить проверку JavaScript, поэтому я доверяю вам самостоятельно написать более полезные проверки в вашем собственном приложении):


​1: 	require ​'rails_helper'​
​- 	
​- 	RSpec.​describe​ ​"PageViews"​ ​do​
​- 	  it ​"shows the number of page views"​ ​do​
​5: 	    visit ​'/welcome'​
​- 	    expect(page.​text​).​to​ match(​/This page has been viewed [0-9]+ times?!/​)
​- 	  ​end​
​- 	
​- 	  it ​"is enhanced with JavaScript on"​, ​js: ​​true​ ​do​ 
​10: 	    visit ​'/welcome'​
​- 	    expect(page).​to​ have_text(​"ENHANCED!"​)
​- 	  ​end​
​- 	​end​
​​ 	   

В соответствии с соглашениями RSpec мы указываем что этот новый scenario (строка 9) будет тпроходить только при включённом JavaScript, на что указывает js: true.

Однако у нас есть проблема. Как вы можете знать, нашим применяемым Capybara по умолчанию драйвером является RackTest, который несмотря на свою скорость не имеет поддержки JavaScript. Если бы мы сейчас исполнили эту свою системную проверку сейчас, она бы отказала, даже несмотря на то (мы в это верим) что эта функция работает.

Чтобы иметь возможность сейчас запустить подлежащую исполнению и основанную на JavaScript спецификацию, нам придётся воспользоваться другим, более развитым драйвером. Существует ряд вариантов:

  • Selenium, который поддерживает различные браузеры, включая Chrome с только что анонсированной выхолощенной поддержкой Chrome.

  • Capybara-webkit, некий драйвер для выхолощенной реализации WebKit из кросс-платформенного инструментария Qt.

  • Poltergeist, некий драйвер для выхолощенного WebKit с применением PhantomJS.

Системные проверки Rails по умолчанию применяют Selenium, поэтому мы остановимся на нём. Я выбираю работу с Chrome через Selenium. Chrome является наиболее популярным браузером рабочих мест и парни говорят хорошие вещи относительно его выхолощенной (headless) поддержки по сравнению с Capybara-webkit.

Для применения Selenium нам потребуется добавить в свой Gemfile соответствующий gem selenium-webdriver :


​ 	group ​:development​, ​:test​ ​do​
​ 	  ​# Call 'byebug' anywhere in the code to stop execution and get a debugger…​
​ 	  gem ​'byebug'​, ​platforms: ​[​:mri​, ​:mingw​, ​:x64_mingw​]
​ 	  gem ​'rspec-rails'​, ​'~> 3.8'​
​ 	  gem ​'capybara'​, ​'~> 3.7'​
»	  gem ​'selenium-webdriver'​, ​'~> 3.14'​
​ 	​end​
 	   

а затем установить его собрав заново наш образ и воссоздав наш контейнер web:


​ 	​$ ​​docker-compose​​ ​​build​​ ​​web​
​ 	​$ ​​docker-compose​​ ​​stop​​ ​​web​
​ 	​$ ​​docker-compose​​ ​​up​​ ​​-d​​ ​​--force-recreate​​ ​​web​
		

Но как мы намерены исполнять Chrome в Docker? Тем же самым образом, которым мы обычно исполняем любое программное обеспечение по Docker - в контейнере. Существуют некие подготовленные образы для запуска обособленных версий Chrome и фактически мы намерены применять один такой образ, поддерживаемый самим Selenium.

Давайте добавим это в свой файл Compose:


​ 	selenium_chrome:
​ 	  image: ​selenium/standalone-chrome-debug​
​ 	  logging:
​ 	    driver: ​none​
​ 	  ports:
​ 	    - ​"​​5900:5900"​
 	   

Это добавит некую новую службу, которую мы выбрали для вызова selenium_chrome, чей контейнер основан на образе selenium/standalone-chrome-debug. Мы выбрали debug вместо стандартной версии этого образа, так как он содержит и запускает некий сервер VNC. Это снабжает нас возможностью визуально наблюдать за исполнением Chrome внутри данного контейнера при помощи клиента VNC - что полезно если вы желаете наблюдать исполнение своих тестов.

Мы также отключили выход из системы, установив для регистрации драйвера none, так как образ Selenium Chrome имеет зашумлённый вывод, который нам нет нужды наблюдать. Мы создали некое соответствие порта с тем, чтобы наш исполняемый внутри своего контейнера по порту 5900 сервер VNC имел возможность доступа извне контейнера по тому же самому порту. Вы можете ознакомиться с документацией Docker относительно дополнительных подробностей по доступным вариантам регистрации.

Давайте запустим свою новую службу для того чтобы Selenium Chrome стал доступным для наших системных проверок:


​ 	​$ ​​docker-compose​​ ​​up​​ ​​-d​​ ​​selenium_chrome​
		
[Замечание]Проблемы при запуске Chrome?

Если вы столкнулись с некой ошибкой при запуске своей службы selenium_chrome, это скорее всего вызвано тем что у вас имеется некий другой клиент VNC, запущенный с портом 5900. Например, в macOS убедитесь что в ваших System Preferences отключено Screen Sharing.

Далее нам придётся настроить Capybara на применение Chrome в неком контейнере. Давайте создадим соответствующий файл spec/support/capybara.rb и добавим следующую необходимую настройку:


​1: 	Capybara.​register_driver​ ​:selenium_chrome_in_container​ ​do​ |app|    
​2: 	  Capybara::Selenium::Driver.​new​ app,                     
​3: 	    ​browser: :remote​,                                     
​4: 	    ​url: ​​"http://selenium_chrome:4444/wd/hub"​,            
​5: 	    ​desired_capabilities: :chrome​                         
​6: 	​end​
 	   

Это выполнит регистрацию нового драйвера в Capybara - с названием :selenium_chrome_in_container - настроенным на применение драйвера Selenium для управления неким удалённым экземпляром Chrome Selenium запущенным в http://selenium_chrome:4444/wd/hub (строка 5). Зачем такой специфичный URL? Selenium выполняет ожидание запросов на входе от клиентов по http://<host>:<port>/wd/hub. Порт 4444 является портом по умолчанию в котором Selenium осуществляет ожидание, а указанное имя хоста selenium_chrome соответствует нашей новой службе в docker-compose.yml, которая будет доступна в нашем исполняющем Chrome контейнере. Вы можете вспомнить в Главе 5, За рамками прикладного приложения: добавляем Redis, что Comopose настраивает имена хостов для доступа к прочим службам Compose.

[Замечание]Пользователи Linux: владение файлами

Убедитесь что вы выполнили chown для данных файлов запустив:


​ 	​$ ​​sudo​​ ​​chown​​ ​​<your_user>:<your_group> ​​-R​​ ​​.​
		

За дополнительными подробностями отсылаем к Дополнению A. Владельцы файлов и полномочия.

  Настройка системных проверок RSpec

Мы создали некий новый драйвер Capybara, но как нам настроить RSpec на его использование?

Во первых нам придётся изменить spec/rails_helper.rb чтобы потребовать загрузки нашего нового драйвера Capybara:


​ 	require ​'rspec/rails'​
​ 	​# Add additional requires below this line. Rails is not loaded until this…​
»	require_relative ​'./support/capybara.rb'​
 	   

Зачастую приложения будут автоматически требовать наличие всех файлов .rb внутри своего каталога spec/support. Если это случай вашего приложения, данный require можно опустить.

Хорошо, что дальше? Напомним что раньше мы применяли токи входа настроек RSpec для установки в качестве драйвера по умолчанию для своих системных проверок rack_test. Теперь нам требуется сделать так, чтобы системные тесты без JavaScript продолжали применять его же, в то время как проверки JavaScript (на которые указывает соответствующий тег js: true в определении конкретного теста) должен применять наш новый драйвер Selenium Chrome Capybara.

Мы можем добиться этого добавив приводимые ниже строки в spec/rails_helper.rb (добавьте их между предыдущей точкой входа before, которую мы уже добавили и самым последним end):


​1: 	config.​before​(​:each​, ​type: :system​, ​js: ​​true​) ​do​
​2: 	  driven_by ​:selenium_chrome_in_container​     
​3: 	  Capybara.​server_host​ = ​"0.0.0.0"​            
​4: 	  Capybara.​server_port​ = 4000                 
​5: 	  Capybara.​app_host​ = ​'http://web:4000'​       
​6: 	​end​
 	   

Системные спецификации, которые определяют js: true будут применять эту новую конфигурацию (а без этого определения будут продолжать применять нашу старую конфигурацию, которая устанавливает драйвер rack_test). Эта новая конфигурация устанавливает новое значение драйвера Capybara (строка 2) в значение selenium_chrome_in_container: наш новый драйвер, который выполняет проверки через наш удалённый браузер Selenium Chrome.

Так как тот драйвер, который исполняет проверки будет запускаться в неком отдельном контейнере, а не в той же самой машине, требуются некие дополнительные настройки. Capybara запустит новый сервер Puma для исполнения некой проверочной версии нашего приложения. Обычно это происходит в локальном хосте в той же самой машине, которая и проверяет исполнение всех тестов и всё работает просто. Здесь же, однако, данной проверочной версии нашего приложения требуется выполнять внешний доступ из своего контейнера Selenium Chrome. Это означает, что нам придётся запустить своё проверочное приложение по некому известному порту - я выбрал порт 4000 (строка 4), но вы можете выбрать некий иной. Кроме того, так же как и когда мы запускали свой сервер с -b 0.0.0.0 для выполнения ожидания по всем адресам, Capybara должна запустить ожидание сервера проверки приложения по всем IP адресам. Именно поэтому мы устанавливаем server_host в 0.0.0.0 (строка 3); без этого наш сервер стартовал бы в localhost и могли бы обслуживаться только входящие запросы внутри самого контейнера.

Наконец, нам требуется сообщить Capybara о необходимости применения URL для того приложения, которое будет работать при подключении внутри соответствующего контейнера Selenium Chrome (строка 5). Помните, Docker устанавливает записи DNS с тем чтобы помочь нам ссылаться на прочие контейнеры служб по названию. Следовательно Selenium Chrome получит доступ к соответствующему запущенному в нашем контейнере Rails приложению для исполнения проверок со значением URL http://web:4000 (ранее мы настроили свой сервер проверок запускаться по порту 4000).

Верно. Мы уже почти готовы попробовать всё это, но вначале мы должны сделать ещё одну вещь. Мы уже сообщили Capybara о необходимости запускать наше проверочное приложение по порту 4000 и мы настроили свои тесты, которые будут исполняться удалённо в нашем контейнере Selenium Chrome выполнять такой доступ. Однако в настоящий момент порт 4000 не доступен извне нашего контейнера web, который в данный момент выставляет только порт 3000.

Давайте исправим это добавив следующую строку в настройки своих web ports в нашем docker-compose.yml:


​ 	ports:
​ 	  - ​"​​3000:3000"​
»	  - ​"​​4000:4000"​
 	   

Чтобы подхватить это изменение нам придётся остановить свой контейнер web и --force-recreate его.


​ 	​$ ​​docker-compose​​ ​​stop​​ ​​web​
​ 	Stopping myapp_web_1 ... done
​ 	​$ ​​docker-compose​​ ​​up​​ ​​-d​​ ​​--force-recreate​​ ​​web​
​ 	Recreating myapp_web_1 ... done
		

Теперь всё должно быть готово для исполнения данных спецификаций:


​ 	​$ ​​docker-compose​​ ​​exec​​ ​​web​​ ​​rspec​​ ​​spec/system/​
​ 	.Capybara starting Puma...
​ 	* Version 3.12.0 , codename: Llamas in Pajamas
​ 	* Min threads: 0, max threads: 4
​ 	* Listening on tcp://0.0.0.0:4000
​ 	.
​ 	
​ 	Finished in 31.17 seconds (files took 8.8 seconds to load)
​ 	2 examples, 0 failures
		

На этот раз когда мы исполнили все тесты, наши зависящие от JavaScript системные спецификации теперь прошли.

  Просмотр исполнения проверок

Как я уже говорил ранее, мы намеренно выбрали образ selenium/standalone-chrome-debug так как он содержит некий исполняемый сервер VNC. Это позволяет нам подключаться и реально просматривать тесты по мере их исполнения в браузере.

Для того чтобы просматривать некий рабочий стол нашего контейнера нам требуется для подключения к нему какой- то клиент VNC. Если вы работаете в Mac? в можете воспользоваться приложением Screen Sharing, которое выполнено в качестве стандартного в maOS. В Linux это зависит от вашего дистрибутива и установленного программного обеспечения, тем не менее установка клиента VNC должна быть достаточно простой с применением вашего диспетчера пакетов. Для Windows имеется целый ряд вариантов, причём часть из них предоставляются в свободном доступе.

Ладно, клиент VNC в готовности? Пройдите далее и запустите его с подключением к vnc://localhost:5900. Порт 5900 является портом по умолчанию для серверов VNC - вы можете помнить что мы выставляли этот порт когда определяли свою службу selenium_chrome в docker-compose.yml. вы должны получить приглашение на ввод пароля, который является "секретным кодом". Введите его теперь и вы должны увидеть открытым некое окно, которое выглядит как- то так:

 

Рисунок 8-1



Это показывает нам тот рабочий стол Linux, который запущен в нашем контейнере службы selenium_chrome. Если всё работает как мы и ожидали, когда мы повторно запустим проверки, мы должны обнаружить что нечто происходит. Давайте закрутим это:


​ 	​$ ​​docker-compose​​ ​​exec​​ ​​web​​ ​​rspec​​ ​​spec/system/​
		

В окне этого VNC клиента вы должны увидеть открытым некий новый браузер Chrome и загрузку страниц нашего приложения (как это показано на рисунке ниже). Наш браузер очень быстро закроется снова, поэтому вы можете добавить sleep 10 посреди своего scenario JavaScript чтобы наблюдать за реальной загрузкой своих страниц.

 

Рисунок 8-2



  Выхолощенный просмотр браузером

Возможность наблюдать наши проверки при их исполнении это очень полезная опция которую следует иметь. Мы обычно нуждаемся в ней когда нечто работает не так как ожидалось и мы бы хотели посмотреть что же происходит на самом деле. Однако в большинстве случаев нам не важно отслеживание исполнения проверок - мы просто хотим чтобы они выполнялись настолько быстро, насколько это возможно для человека (или для данного компьютера). По этой причине мы обычно исполняем свои системные спецификации в выхолощенном (headless) режиме, без отображения в действительности некого окна браузера. Без накладных расходов отрисовки реального UI эти тесты могут исполняться намного быстрее.

Для достижения этого давайте зарегистрируем некий второй драйвер в spec/support/capybara.rb для запуска Selenium Chrome в выхолощенном режиме:


»	require ​"selenium/webdriver"​
​ 	
​ 	Capybara.​register_driver​ ​:selenium_chrome_in_container​ ​do​ |app|
​ 	  Capybara::Selenium::Driver.​new​ app,
​ 	    ​browser: :remote​,
​ 	    ​url: ​​"http://selenium_chrome:4444/wd/hub"​,
​ 	    ​desired_capabilities: :chrome​
​ 	​end​
​ 	
»	Capybara.​register_driver​ ​:headless_selenium_chrome_in_container​ ​do​ |app|
»	  Capybara::Selenium::Driver.​new​ app,
»	    ​browser: :remote​,
»	    ​url: ​​"http://selenium_chrome:4444/wd/hub"​,
»	    ​desired_capabilities: ​Selenium::WebDriver::Remote::Capabilities.​chrome​(
»	      ​chromeOptions: ​{ ​args: ​​%w(headless disable-gpu)​ }
»	    )
»	​end​
 	   

Это новое определение драйвера Capybara по существу идентично самому первому; единственное отличие состоит в том, что мы определяем headless и disable-gpu при запуске Chrome. Обратите внимание, что нам также понадобится потребовать selenium/webdriver так как нам нужно применять класс Selenium::WebDriver::Remote::Capabilities.

Для переключения на применение выхолощенного драйвера нам придётся изменить соответствующую настройку RSpec, которую мы добавили в spec/rails_helper.rb для использования этого драйвера в наших системных спецификациях JavaScript:


​ 	config.​before​(​:each​, ​type: :system​, ​js: ​​true​) ​do​
»	  driven_by ​:headless_selenium_chrome_in_container​
​ 	
​ 	  Capybara.​server_host​ = ​"0.0.0.0"​
​ 	  Capybara.​server_port​ = 4000
​ 	  Capybara.​app_host​ = ​'http://web:4000'​
​ 	​end​
 	   

Теперь если вы повторно запустите свои системные спецификации:


​ 	​$ ​​docker-compose​​ ​​exec​​ ​​web​​ ​​rspec​​ ​​spec/system/​
		

Вы на самом деле не увидите свой браузер появляющимся в соответсвующем окне VNC, однако эта проверка пройдёт.

Отладка

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

Допустим у нас имеется проблема с нашим welcome_controller.rb. Давайте применим отладчик byebug, который включён в качестве стандартного в некое приложение Rails. Давайте добавим некую точку прерывания byebug в свой welcome_controller.rb:


​ 	​class​ WelcomeController < ApplicationController
​ 	  ​def​ ​index​
​ 	    redis = Redis.​new​(​host: ​​"redis"​, ​port: ​6379)
​ 	    redis.​incr​ ​"page hits"​
​ 	
​ 	    @page_hits = redis.​get​ ​"page hits"​
»	    byebug
​ 	  ​end​
​ 	​end​
 	   

Давайте остановим свой сервер Rails:


​ 	​$ ​​docker-compose​​ ​​stop​​ ​​web​
		

Когда нам требуется некий интерактивный сеанс с контейнером, вместо того чтобы применять docker-compose up мы используем docker-compose run. Давайте запусти некий интерактивный сеанс со своим сервером web подобно следующему:


​ 	​$ ​​docker-compose​​ ​​run​​ ​​--service-ports​​ ​​web​
		

По умолчанию docker-compose run будет игнорировать соответствие портов определённое в файле docker-compose.yml для данной службы. Установка параметра service-ports изменяет такое поведение и гарантирует установку их соответствия; без этого наш сервер Rails не смог бы быть доступным по порту 3000 из нашего браузера.

Теперь посетим http://localhost:3000/welcome в своём браузере: данная страница будет прервана при достижении точки прерывания byebug. Вернувшись в свой терминал вы сможете обнаружить дожидающимся вас некий знакомый интерфейс byebug:


​ 	=> Booting Puma
​ 	=> Rails 5.2.2 application starting in development
​ 	=> Run `rails server -h` for more startup options
​ 	Puma starting in single mode...
​ 	...
​ 	[2, 11] in /usr/src/app/app/controllers/welcome_controller.rb
​ 	
​ 	    2:   def index
​ 	    3:     redis = Redis.new(host: "redis", port: 6379)
​ 	    4:     redis.incr "page hits"
​ 	    5:
​ 	    6:     @page_hits = redis.get "page hits"
​ 	    7:     byebug
​ 	=>  8:   end
​ 	    9: end
​ 	   10:
​ 	   11:
​ 	(byebug)
		

Не стесняйте себя ничем чтобы поэкспериментировать здесь с целью проверки как это всё работает; например, вы можете вывести значение переменной @page_hits чтобы увидеть её значение. Когда вы будете готовы завершить, нажмите c (для "continue"), затем Enter; данный запрос продолжится как обычно и ваша страница должна отобразиться в вашем браузере. Нажмите Ctrl-C для остановки своего контейнера web.

Прежед чем переместиться, не забудьте удалить установленную точку прерывания byebug из welcome_controller.rb, а затем перезагрузить свой сервер web снова:


​ 	​$ ​​docker-compose​​ ​​up​​ ​​-d​​ ​​web​
		
[Замечание]Сервер Rails не запускается?

Если Rails отказывается запускаться по причине того что он полагает что сервер уже запущен, удалите tmp/pids/server.pid в своей локальной машине и попробуйте снова. Обработку этого мы рассмотрим в лучшем виде в Главе 9, Расширенное управление Gem.

Если вы применяете IntelliJ или RubyMine, у них имеется встроенная поддержка применения Ruby через docker-compose, включая поддержку для их отладчика.

Беглый обзор

На текущий момент использование Docker должно начать казаться довольно знакомым. Фактически, большая часть этой главы была стандартной обыденностью, которую мы выполняли в Rails - Docker ушёл с нашего пути. Ситуация стала немного сложнее, когда дело дошло до тестов, требующих JavaScript, но наличие готового образа с Chrome под механизмом Selenium сделало их установку проще простого - вот где восиял Docker.

Итого:

  1. Мы настроили и установили RSpec.

  2. Мы рассмотрели как запускать свои спецификации в среде Docker.

  3. Мы установили системные спецификации и выполнили проверки с применением своего стандартного драйвера RackTest.

  4. Мы получили свои системные спецификации работающими даже когда необходим JavaScript, настроив Capybara на использование браузера Chrome под механизмом Selenium, запускаемом в отдельном контейнере.

  5. Мы ускорили свои системные проверки JavaScript настроив выхолощенный Chrome в обычном использовании.

  6. Мы изучили как отлаживать наше приложение, даже когда оно исполняется внутри некого контейнера.

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