Docker + Capybara + Selenium Chrome Driver

If your Rails app needs extracting data from a website, or if you want to write tests for your features, you might think about a solution like  Capybara.  If you take a look at Capybara github homepage, Capybara can integrate with many drivers, the default one is rack_test, the lightest driver but it does not support executing javascript. There are many other good drivers like capybara-webkit and poltergeist to integrate capybara with phantomjs. I have tried poltergeist but I experienced many problems as listed on the poltergeist github homepage. Thus, I decided to give a try with other popular web driver selenium install with famous  chrome browser.

In this blog I will note some steps to have chrome driver setup together with your Rails inside your Docker as I guess using Docker to setup Rails is quite common now.

    1. Run Docker pull command
      docker pull selenium/standalone-chrome
    2. You need to add extra gem
      gem 'selenium-webdriver'
    3. You need another Docker container which hook to hub. You can do so by adding below code to docker-compose.yml
services:
  web:
    image: selenium/node-chrome:3.12.0-boron    
    depends_on:
      - hub
    environment:
      HUB_HOST: hub

  hub:
    image: selenium/hub:3.12.0-boron
    ports:
      - "4444:4444"
    # .....

Update your docker by

docker-compose build

How about your code? The important thing is that you need to tell Capybara to hook to hub in order to open chrome headless browser when registering driver

require 'selenium-webdriver'
# .....

def instantiate_session
        selenium_url = "http://hub:4444/wd/hub"

        Capybara.register_driver :chrome do |app|
          options = Selenium::WebDriver::Chrome::Options.new(
            args: %w[headless disable-gpu no-sandbox]
          )
          Capybara::Selenium::Driver.new(app, url: selenium_url, browser: :chrome, options: options)
        end

        @session = Capybara::Session.new(:chrome)
        @session.visit(https://google.com)

Have fun!

RSpec Custom Devise API

Rails developers may quite familiar with Devise and RSpec. In a recent application, I need to custom Devise controllers. And of course, I need to write RSpec these. In this blog post, I will tell about RSpec custom Devise Request.
First, because I wanna create custom Devise, I need to set custom routes, like this:

  devise_for :users, :controllers => { :registrations => "users/registrations", :sessions => "users/sessions"}

And this is my custom devise for being able to response with Json request:

  # users/sessions_controller.rb
  # ofcourse, require no authentication for this
  def create
    build_resource
    resource = User.find_for_database_authentication(:email=>params[:user][:email])

    return invalid_login_attempt unless resource

    if resource.valid_password?(params[:user][:password])
      sign_in("user", resource)
      render :json=> {:success=>true, :user => resource}
      return
    end
    invalid_login_attempt
  end

Ok, I have an api for custom login via: POST: 'users/sign_in'

And this is my RSpec:

# spec/request/sessions_requests_spec.rb
require "rails_helper"

RSpec.describe "Post #create", type: :request do
  before(:all) do
    User.create(
        email: "test@example.com",
        password: 'password',
        password_confirmation: 'password',
    )
  end

  it "responds successfully with an HTTP 201 status code" do
    user_params = {user: { email: "test@example.com", password: "password"}}.to_json
    post "users/sign_in", user_params, request_headers

    expect(response).to be_success
    expect(response).to have_http_status(200)

    expect(json).to be_has_key("user")
    expect(json["user"]).to be_has_key("authentication_token")
  end

  it "responds unsuccessfully with an HTTP 401 status code" do
    user_params = {user: { email: "test@example.com", password: "wrong password"}}.to_json
    post "users/sign_in", user_params, request_headers

    expect(response).not_to be_success
    expect(response).to have_http_status(401)

    expect(json["message"]).to eql("Error with your login or password")
  end
end

In this RSpec, I used two helpers variables, json and request_headers.
I created them in a support modules for DRY spec.

# spec/supports/request_helpers.rb
module Requests
  module JsonHelpers
    def json
      @json ||= JSON.parse(response.body)
    end

    def request_headers
      {
          "Accept" => "application/json",
          "Content-Type" => "application/json"
      }
    end
  end
end

And add these helpers in configure file:

# spec/spec_helper.rb
require 'supports/request_helpers'
...
config.include Requests::JsonHelpers, type: :request

TDD Practices with RSpec in Creating Web Service

TDD process
TDD process

 

I. TDD

I think that many of you have heard about TDD, a methodology which is becoming more famous and popular today. Although you may appreciate the process of software development, you may not have opportunity to practice TDD. This article will help you to do that. In this post, I want to give you an overview of TDD, and a tool called RSpec for writing automation testing script in Ruby.

TDD stands for ‘Test-driven development’, a software development process in which we repeat the cycle: Write test first → Code → Refactoring through the life time of project. As you can see from the above picture, first create test script, run the scripts and you should see the ‘red’ results. Then you write code to make the tests pass and you should see ‘green’ results. Finally, after passing the tests, you should refactoring your code to make it cleaner but remember keeping the ‘green’ results. That’s the reason you would be familiar with the ‘Red – Green – Refactoring’ term when talking about TDD.

Continue reading TDD Practices with RSpec in Creating Web Service