神刀安全网

Dockerized Rails Capybara tests on top of Selenium

If you use Docker to deploy your Rails application you may want to use the same infrastructure to run your tests.

However the setup of your Selenium browser tests is far from obvious with Rails and Docker and may generate some confusion .

The short answer is available in this repository on Github. For the long answer keep reading this blog post for a step by step tutorial!

Dockerize Rails

Let’s start by creating a Docker container to run your application. First we create a simple rails hello world app with the following commands:

# In your console railsnew test_app 

# routes.rb root 'hello_world#index' 

# app/controller/hello_world_controller.rb class HelloWorldController < ApplicationController end 

# app/views/hello_world/index.html.erb HelloWorld 

then we add the following to your Gemfile :

group :test do   gem 'capybara'   gem 'rspec-rails'   gem 'selenium-webdriver' end 

Run bundle install and bundle exec rails server to make sure that our app is accessible at http : //locahost:3000 . Also run  rails generate rspec : install to initialize the spec directory.

We will now use the Gemfile and the  Gemfile . lock to set up of our Docker container. Let’s create a Dockerfile in the root of the project with the following:

FROMruby:latest RUNapt-getupdate -qq && apt-getinstall -y build-essentialnodejs   RUNmkdir /app WORKDIR /app COPYGemfileGemfile COPYGemfile.lockGemfile.lock RUNbundleinstall   CMD ["rails", "server", "-b", "0.0.0.0"] 

The Dockerfile downloads the Ruby image called ruby : latest and installs  build essential nodejs and the gems specified in our Gemfile including Rails.

Connect to a remote Selenium server

To run our tests we need a Selenium server running somewhere. By default the capybara and  selenium webdriver gems spawn the selenium server automatically for us when we run our specs. But nothing stops us to select a different remote selenium server! Let’s do that.

First go to http://www.seleniumhq.org/download/ , download the latest tar and run the server with:

java -jarselenium-server-standalone-.jar 

Now connect Capybara with the Selenium server we just started. Inside rails_helper . rb add the following:

Capybara.javascript_driver = :selenium_remote_firefox Capybara.register_driver "selenium_remote_firefox".to_sym do |app|   Capybara::Selenium::Driver.new(app, browser: :remote, url: "http://localhost:4444/wd/hub", desired_capabilities: :firefox) end 

This will instruct Capybara to connect to the remote Selenium instance we are running. Finally add to the RSpec configuration the following lines:

RSpec.configuredo |config|   [...otherconf...]     config.before(:each) do     Capybara.app_host = "http://localhost:#{Capybara.current_session.server.port}"   end     config.after(:each) do     Capybara.reset_sessions!     Capybara.use_default_driver     Capybara.app_host = nil   end end 

This will tell the Selenium server that the app it must visit is the one spawned by Capybara in our localhost. If you run  bundle exec rspec you should see the following in the Selenium server output:

19:02:02.207 INFO - SeleniumServeris upand running 19:09:42.174 INFO - Executing: [new session: Capabilities [{rotatable=false, nativeEvents=false, browserName=firefox, takesScreenshot=true, javascriptEnabled=true, version=, platform=ANY, cssSelectorsEnabled=true}]]) 19:09:42.198 INFO - Creating a new session for Capabilities [{rotatable=false, nativeEvents=false, browserName=firefox, takesScreenshot=true, javascriptEnabled=true, version=, platform=ANY, cssSelectorsEnabled=true}] 19:09:46.069 INFO - Done: [new session: Capabilities [{rotatable=false, nativeEvents=false, browserName=firefox, takesScreenshot=true, javascriptEnabled=true, version=, platform=ANY, cssSelectorsEnabled=true}]] 19:09:46.081 INFO - Executing: [get: http://localhost:57193/]) 19:09:46.940 INFO - Done: [get: http://localhost:57193/] 19:09:46.953 INFO - Executing: [findelements: By.xpath: /html]) 19:09:47.046 INFO - Done: [findelements: By.xpath: /html] 19:09:47.059 INFO - Executing: [is displayed: 0 [[FirefoxDriver: firefoxonMAC (e536aaa9-930d-e544-849f-35739e1519c5)] -> xpath: /html]]) 19:09:47.088 INFO - Done: [is displayed: 0 [[FirefoxDriver: firefoxonMAC (e536aaa9-930d-e544-849f-35739e1519c5)] -> xpath: /html]] 19:09:47.094 INFO - Executing: [gettext: 0 [[FirefoxDriver: firefoxonMAC (e536aaa9-930d-e544-849f-35739e1519c5)] -> xpath: /html]]) 19:09:47.108 INFO - Done: [gettext: 0 [[FirefoxDriver: firefoxonMAC (e536aaa9-930d-e544-849f-35739e1519c5)] -> xpath: /html]] 19:09:47.122 INFO - Executing: [deleteallcookies]) 19:09:47.136 INFO - Done: [deleteallcookies] 19:09:47.142 INFO - Executing: [get: about:blank]) 19:09:47.178 INFO - Done: [get: about:blank] 19:09:47.184 INFO - Executing: [findelements: By.xpath: /html/body/*]) 19:09:47.194 INFO - Done: [findelements: By.xpath: /html/body/*] 19:09:47.201 INFO - Executing: [deletesession: bde150ad-b376-45ea-b46b-5e6c82af97fa]) 19:09:47.349 INFO - Done: [deletesession: bde150ad-b376-45ea-b46b-5e6c82af97fa] 

The server correctly received the calls from Capybara, visited the test app and completed the test successfully.

Dockerize Selenium

The next step is to dockerize our Selenium server to run our browser tests inside a Docker container instead of our localhost . First, we need to create a docker compose file to run our application:

version: '2' services:   test:     build: .     command: bundleexecrspec     container_name: test_app     environment:       - SELENIUM_REMOTE_HOST=selenium     volumes:       - .:/app     depends_on:       - selenium   selenium:     image: selenium/standalone-firefox     container_name: selenium 

The app has two services running inside the corresponding containers. The test service is running the rails container specified in our Dockerfile . The build entry specifies the location of the  Dockerfile . The command entry specifies the command to execute when the service is invoked. In our case we want to run our tests. container_name , environment and depends_on are self explicative. Finally the volumes instruction link our code into the container. Finally, the Selenium service is pretty easy to set up because there is a dockerized version of the server ready for us in  selenium / standalone firefox !

Let’s now go back to our rails_helper . We need the following to use the Selenium remote server:

if ENV['SELENIUM_REMOTE_HOST']   Capybara.javascript_driver = :selenium_remote_firefox   Capybara.register_driver "selenium_remote_firefox".to_sym do |app|     Capybara::Selenium::Driver.new(       app,       browser: :remote,       url: "http://#{ENV['SELENIUM_REMOTE_HOST']}:4444/wd/hub",       desired_capabilities: :firefox)   end end 

Notice that Docker by default makes the Selenium service available at the http : //selenium host using the name specified in the docker-compose file. The port is the default one.

The RSpec configuration changes as follows:

RSpec.configure do |config|   [...other config...]     config.before(:each) do     if /selenium_remote/.match Capybara.current_driver.to_s       ip = `/sbin/ip route|awk '/scope/ { print $9 }'`       ip = ip.gsub "/n", ""       Capybara.server_port = "3000"       Capybara.server_host = ip       Capybara.app_host = "http://#{Capybara.current_session.server.host}:#{Capybara.current_session.server.port}"     end   end     config.after(:each) do     Capybara.reset_sessions!     Capybara.use_default_driver     Capybara.app_host = nil   end end 

You may be surprised by these four magic lines:

ip = `/sbin/ip route|awk '/scope/ { print $9 }'` ip = ip.gsub "/n", "" Capybara.server_port = "3000" Capybara.server_host = ip 

these instructions retrieve the ip address of the container and pass it to Capybara and in turn to Selenium such that it will be able to retrieve the pages to test. In the basic Selenium remote example this configuration was not necessary because our test app was running on our localhost.

Lunch the tests!

To run our tests at this point all we need is:

docker-composeup 

and you should see something similar to the following:

:capybara-on-dockerized-selenium (master *+)$ docker-composeup Creatingnetwork "capybaraondockerizedselenium_default" withthedefault driver Creatingselenium Creatingtest_app Attachingto selenium, test_app selenium    | 11:33:18.295 INFO - Launching a standaloneSeleniumServer selenium    | 11:33:18.413 INFO - Java: OracleCorporation 25.03-b03 selenium    | 11:33:18.417 INFO - OS: Linux 4.4.8-boot2dockeramd64 selenium    | 11:33:18.471 INFO - v2.53.0, withCorev2.53.0. Builtfromrevision 35ae25b selenium    | 11:33:18.825 INFO - Driverproviderorg.openqa.selenium.ie.InternetExplorerDriverregistrationis skipped: selenium    | registration capabilities Capabilities [{ensureCleanSession=true, browserName=internetexplorer, version=, platform=WINDOWS}] doesnot matchthecurrentplatformLINUX selenium    | 11:33:18.836 INFO - Driverproviderorg.openqa.selenium.edge.EdgeDriverregistrationis skipped: selenium    | registration capabilities Capabilities [{browserName=MicrosoftEdge, version=, platform=WINDOWS}] doesnot matchthecurrentplatformLINUX selenium    | 11:33:18.837 INFO - Driverclass not found: com.opera.core.systems.OperaDriver selenium    | 11:33:18.840 INFO - Driverprovidercom.opera.core.systems.OperaDriveris not registered selenium    | 11:33:18.850 INFO - Driverproviderorg.openqa.selenium.safari.SafariDriverregistrationis skipped: selenium    | registration capabilities Capabilities [{browserName=safari, version=, platform=MAC}] doesnot matchthecurrentplatformLINUX selenium    | 11:33:18.860 INFO - Driverclass not found: org.openqa.selenium.htmlunit.HtmlUnitDriver selenium    | 11:33:18.861 INFO - Driverproviderorg.openqa.selenium.htmlunit.HtmlUnitDriveris not registered selenium    | 11:33:19.151 INFO - RemoteWebDriverinstancesshouldconnectto: http://127.0.0.1:4444/wd/hub selenium    | 11:33:19.157 INFO - SeleniumServeris upand running test_app    | RunningviaSpringpreloaderin process 17 test_app    | /app/db/schema.rbdoesn't existyet. Run `rakedb:migrate` to createit, then try again. If youdo not intendto use a database, youshouldinsteadalter /app/config/application.rbto limittheframeworksthatwillbeloaded. selenium    | 11:33:33.592 INFO - Executing: [new session: Capabilities [{rotatable=false, nativeEvents=false, browserName=firefox, takesScreenshot=true, javascriptEnabled=true, version=, platform=ANY, cssSelectorsEnabled=true}]]) selenium    | 11:33:33.613 INFO - Creating a new session for Capabilities [{rotatable=false, nativeEvents=false, browserName=firefox, takesScreenshot=true, javascriptEnabled=true, version=, platform=ANY, cssSelectorsEnabled=true}] selenium    | 11:33:37.954 INFO - Done: [new session: Capabilities [{rotatable=false, nativeEvents=false, browserName=firefox, takesScreenshot=true, javascriptEnabled=true, version=, platform=ANY, cssSelectorsEnabled=true}]] selenium    | 11:33:37.966 INFO - Executing: [get: http://172.18.0.3:3000/]) selenium    | 11:33:39.078 INFO - Done: [get: http://172.18.0.3:3000/] selenium    | 11:33:39.105 INFO - Executing: [findelements: By.xpath: /html]) selenium    | 11:33:39.289 INFO - Done: [findelements: By.xpath: /html] selenium    | 11:33:39.303 INFO - Executing: [is displayed: 0 [[FirefoxDriver: firefoxonLINUX (178058bb-7190-472a-b65c-69b21bbdff3e)] -> xpath: /html]]) selenium    | 11:33:39.402 INFO - Done: [is displayed: 0 [[FirefoxDriver: firefoxonLINUX (178058bb-7190-472a-b65c-69b21bbdff3e)] -> xpath: /html]] selenium    | 11:33:39.410 INFO - Executing: [gettext: 0 [[FirefoxDriver: firefoxonLINUX (178058bb-7190-472a-b65c-69b21bbdff3e)] -> xpath: /html]]) selenium    | 11:33:39.452 INFO - Done: [gettext: 0 [[FirefoxDriver: firefoxonLINUX (178058bb-7190-472a-b65c-69b21bbdff3e)] -> xpath: /html]] selenium    | 11:33:39.464 INFO - Executing: [deleteallcookies]) selenium    | 11:33:39.485 INFO - Done: [deleteallcookies] selenium    | 11:33:39.494 INFO - Executing: [get: about:blank]) selenium    | 11:33:39.576 INFO - Done: [get: about:blank] selenium    | 11:33:39.588 INFO - Executing: [findelements: By.xpath: /html/body/*]) selenium    | 11:33:39.617 INFO - Done: [findelements: By.xpath: /html/body/*] test_app    | . test_app    | test_app    | Finishedin 7.03 seconds (filestook 13.39 secondsto load) test_app    | 1 example, 0 failures test_app    | selenium    | 11:33:39.631 INFO - Executing: [deletesession: 859dc787-21dc-4715-80f2-d0305f06bce3]) selenium    | 11:33:39.758 INFO - Done: [deletesession: 859dc787-21dc-4715-80f2-d0305f06bce3] test_appexitedwithcode 0 

Conclusion

In this blog post I have presented how to run your Selenium tests in a Docker environment. For people new to Docker it may be quite hard to fit all the pieces together and I hope this will be helpful to some fellow engineer.

If you enjoyed this blog post you can also follow me on twitter .

Looking forward to hearing your comments!

References

Also published on Medium .

转载本站任何文章请注明:转载至神刀安全网,谢谢神刀安全网 » Dockerized Rails Capybara tests on top of Selenium

分享到:更多 ()

评论 抢沙发

  • 昵称 (必填)
  • 邮箱 (必填)
  • 网址