Testing AJAX with Capybara and Selenium

In the past days I migrated my tests from WebRat to Capybara and I wrote a couple new acceptance tests with RSpec, Capybara and the selenium-webdriver. All in one it’s pretty cool.

You can just keep writing your acceptance tests as usual with RSpec and Capybara. Here is a small example.

describe "Empty Payment History", :js => true do
  it "shows correct message when there's no history" do
    visit "/settings/payments"
    have_css "#payment_history", text: "You dont have any Payment history"
  end
end

This test is sending a request to “/settings/payments” and is testing if on the page the CSS class “payment_history” occurs. Pretty easy. This you could also do with WebRat. But the magic is in the first line. “:js => true” that tells Capybara that it should execute the test with the selenium-webdriver. That will basically start your browser (Firefox) and you can see how the test gets executed. This is not possible with WebRat.

It’s just getting a little bit tricky if you do a lot of AJAX requests on the page. In the Capybara documentation they write that you should use the “find” methods, because they wait until an element appears on the page. That didn’t worked out for me. The test always failed. Somebody on Stackoverflow wrote that this construct would work for AJAX pages.

within('#payment_history') do
  page.all('a',  :text => 'View receipt')
end

And he was right! This test always succeeded. ALWAYS! Even if the test was completely wrong! :-D Yeah. Very funny! *LOL* Seems like a bug. I did a little bit more research and finally I found a solution which worked correctly.

using_wait_time 10 do
  page.should have_content("View receipt")
end

With “using_wait_time” you can force Selenium to wait for a couple seconds, until the AJAX requests are done. That finally worked out and the tests are working now correctly.

Don’t use Webrat anymore

Webrat is a testing Framework for Ruby. In general it is pretty cool, but DEAD! The last version was released more than 2 years ago. And there are only 200 GEMs referencing it.

Screen Shot 2013-05-19 at 1.58.05 PM

The newest PullRequests on GitHub are 1 year old! Not an active project! Don’t use dead projects!

I moved my tests to Capybara. This project is more active. VersionEye shows that the newest version was released 1 month ago and there are almost 2300 GEMs referencing it.

Screen Shot 2013-05-19 at 2.01.56 PM

And the newest PullRequests on GitHub are only 4 days old. That all shows me that it’s still active and I feel better if I know that there developers fixing bugs :-)

Moving Tests from Webrat to Capybara

I one of my applications I had a bunch of tests written with RSpec and Webrat. Unfortunately it seems that Webrat is not longer maintained actively anymore. That’s why it is a good decision to move to Capybara, an active Test Framework for Ruby.

The Migration was so far pretty smooth. Most time it was a simple replacement of code. Most time I had to replace something like this:

response.should contain("STRING_TO_TEST")

With this :

response.body.should match("STRING_TO_TEST")

Otherwise assertions like this caused problems:

response.status.should == 401

That worked again as soon I wrote it like this here:

response.status.should eq(401)

2 times I got the error message that response is nil. That I could resolve by assigning it explicitly.

response = post @project_uri, {:api_key => @user_api.api_key}, "HTTPS" => "on"
response.status.should eq(403)

Otherwise it worked out pretty good.

Testing SSL with Capybara and Selenium

I am using Capybara with Selenium as JS engine to write acceptance tests for a Ruby on Rails application. In some controllers I am forcing SSL with the “force_ssl” filter from Rails. By running the tests with Selenium this caused some problems. Selenium is launching Firefox and calls the URL https://127.0.0.1:3000/signin. Of course there is no SSL for localhost! This causes an error and the test fails.

I did some research for this. There are some tickets on GitHub and StackOverflow to this. but nothing what actually solves the core problem. For right now I just solved it, with running the filter only in production mode and not in test mode.

force_ssl if Rails.env.production?

Now Firefox is launching on http://127.0.0.1:3000/signin.

undefined method `visit’ for RSpec with Capybara

I just started to write an acceptance test with capybara. I followed the code example on the GitHub Page and I got this odd error:

Failure/Error: visit 'http://127.0.0.1:3000/signin'
 NoMethodError:
 undefined method `visit' for #<RSpec::Core::ExampleGroup::Nested_1::Nested_1:0x007fda48e0f680>

I placed my test in “spec/requests”. After some research I found out that the new Capybara GEM expects the test to be in “spec/features”. After I moved my test file to the right directory it worked perfectly.

How to mock the GitHub API

If you write code against the GitHub API you have to mock it somehow. Otherwise it can be tricky to test it. Here is how I did it. I found this great GEM FakeWeb. With this GEM you can fake Web Requests. It allows you to register URLs with fix responses. Her is an example:

FakeWeb.register_uri(:get, "https://github.com/", :body => "Awesome")

If you do now a HTTP request to github.com you will get “Awesome” as response.

Net::HTTP.get(URI.parse("http://github.com"))
=> "Awesome"

Here is how I mocked the GitHub OAuth login precess. I just registered this 2 URLs:

FakeWeb.register_uri(:get, "https://github.com/login/oauth/access_token?client_id=#{Settings.github_client_id}&client_secret=#{Settings.github_client_secret}&code=123", :body => "token=token_123")
FakeWeb.register_uri(:get, "https://api.github.com/user?access_token=token_123", :body => "{\"id\": 1, \"email\": \"test@test.de\"}")

And that’s it. With that you can now test your callback.


get "/auth/github/callback?code=123"
assert_response :success

I am using RSpec for testing. Let me know if you have questions.

Determine scopes for given GitHub Token

If you have a given token from GitHub and you want to know which scopes it has you have to check the Headers. Just use the token for any resource on the GitHub API and double check the headers of the response. In the headers the “x-oauth-scopes” field tells you the which scopes the token has.

Here is a small example with Ruby and HTTParty.


response = HTTParty.get("https://api.github.com/user?access_token=#{token}", :headers => {"User-Agent" => A_USER_AGENT } )
response.headers['x-oauth-scopes']

If you HTTParty, party hard! ;-)

GitHub API : User Agent Now Mandatory

GitHub now enforces the User Agent. If you do API calls without user agent you will get a 403 error message back. Check out this post: 
http://developer.github.com/changes/2013-04-24-user-agent-required/
.

If you are using the HTTParty GEM in Ruby to do the API calls you have to set the headers. Here an example:

body = HTTParty.get(url, :headers => {"User-Agent" => "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_8_2) AppleWebKit/537.17 (KHTML, like Gecko) Chrome/24.0.1309.0 Safari/537.17" } ).response.body

VersionEye API : Dependency stable

At the last DevCamp in KA I had some good discussion about the VersionEye API. That’s why I enhanced it a little bit. I just introduced a new attribute to the project dependencies. The “stable” attribute. If this is true than that means that the newest version is stable, otherwise the newest version is unstable. In that case it could be an alpha or beta version. If this is the case we display the dependency with an yellow background instead of a red background.

Screen Shot 2013-03-25 at 11.19.46 AM Here is the color code we are using at VersionEye: 

if unknown == true
  return "gray"
elsif outdated == true && stable == true
  return "red"
elsif outdated == true && stable == false
  return "yellow"
else
  return "green"
end

Ruby Link Checker

This code checks if the given link exists and returns an HTTP Code 200.


def self.link_ok? link
  url = URI.parse( link )
  req = Net::HTTP.new(url.host, url.port)
  req.use_ssl = true
  req.verify_mode = OpenSSL::SSL::VERIFY_NONE
  res = req.request_head(url.path)
  res.code.to_i == 200
rescue => e
  p "ERROR #{e.message}"
  return false
end

Parsing a CSV in Ruby

Parsing is CSV file in Ruby is very easy. Check out this code:

require "csv"
csv_file_path = "ABSOLUTE_PATH_TO_YOUR_CSV_FILE"
file = File.open(csv_file_path, "r:ISO-8859-1")
csv = CSV.parse file

That’s it. This is how you get the first row:

csv.first

This is how you get the first column of the first row:

csv.first.first

And this is how you iterate over all rows:

csv.each do |row|
  p row
end

Easy! Right?

How to get a list of all licenses for your Software Project

Through the new VersionEye API you can get a list of all licenses for your software project. VersionEye now supports 7 different package managers. In this example I will use a Ruby Gemfile to demonstrate how easy it is to get the license list.

First of all you need to signup at VersionEye to get your API KEY. It’s free and takes less than 30 seconds. With your API KEY you can take advantage of the VersionEye API.

The “/api/v1/projects.json” resource supports file uploads via the API.

Screen Shot 2013-02-16 at 3.39.11 PM

Just send your Gemfile via POST to this resource and receive a JSON object as response with all the dependencies in the file.

Screen Shot 2013-02-16 at 3.39.35 PM

In the JSON response you will receive an “project_key”. This project key is unique per user. You can use it to fetch all licenses for the project. Just send a GET request to “/api/v1/projects/PROJECT_KEY/licenses.json?api_key=API_KEY” and you will receive the license list as JSON.

Screen Shot 2013-02-16 at 3.40.14 PM

That’s it. You need only 2 Requests to get the licenses list. Let me know how you like it.

Ruby on Rails + MySQL on CloudControl

CloudControl is a pretty cool StartUp from Berlin. It is a PaaS Solution like Heroku.  CloudControl is hosting on AWS in Ireland. They are more flexible than Heroku. Beside the number of web processes you can choose how much RAM each instance should have. This is a cool features what you don’t get on Heroku. And you can have different environments on CloudControl for 1 single app. Check out their page: 
https://www.cloudcontrol.com/

Cloud-Control-logo-centered

I want to show here how you get started with your Ruby on Rails on CloudControl. First of all you have to sign up at CloudControl:  
https://console.cloudcontrolled.com/

I assume you are familiar with GIT, Ruby and Linux/Mac OS X. First of all you have to install the CloudControl command line tool.

$ sudo easy_install pip
$ sudo pip install cctrl

The command line tool will be installed via pip, the python package manager. the CloudControl command line tool works similar to the Heroku command line tool. Let’s add your public SSH Key:

cctrluser key.add

In that way you don’t have to enter every time your credentials.

As next switch to the app root directory of your Rails App you want to deploy on CloudControl. Create an app on CloudControl:

cctrlapp APP_NAME create ruby

And now push your code to CloudControl:

cctrlapp APP_NAME push

The command above pushes your code but it doesn’t deploy it. Use this command to make a deployment:

cctrlapp APP_NAME deploy

Now check your app on :

http[s]://APP_NAME.cloudcontrolled.com

It is very likely that it is not running :-) No problem. You have to make some small changes to your Rails App. First of all, if you create an app on CloudControl it is without any database. That is a big difference to Heroku. Of course you can add a MySQL or Postgres DB as AddOn. Let’s add a MySQL:

cctrlapp APP_NAME/default addon.add mysqls.free

Don’t forget to add the “mysql2” GEM to your Gemfile. This is the driver for MySQL.

Now you have to customise your database.yml like this:

production:
  adapter: mysql2
  encoding: utf8
  database: <%= "'#{ ENV['MYSQLS_DATABASE'] }'" %>
  host: <%= "'#{ ENV['MYSQLS_HOSTNAME'] }'" %>
  port: <%= ENV["MYSQLS_PORT"] %>
  username: <%= "'#{ ENV['MYSQLS_USERNAME'] }'" %>
  password: <%= "'#{ ENV['MYSQLS_PASSWORD'] }'" %>
  pool: 100
  timeout: 5000

All right. One more step. You have to have a “Procfile” in the root of your Rails App with this content:

web: bundle exec rails s -p $PORT

That’s it. Now try again:

cctrlapp APP_NAME push
cctrlapp APP_NAME deploy

And check the URL :

http[s]://APP_NAME.cloudcontrolled.com

For me it worked quiet well. Let me know how it works for you.

Getting a list of all Licenses in a Project

In a regular project you are using a handfull Software Libraries. Every Library in your project can use a different license. To get a list of all licenses which are used in your project can be difficult. You can double check the license for every single library in your project manually. But that is time intensive and a pain in the ass!

VersionEye is offering a quick solution for that. You can get the license list in less than 1 minute. After your login you are in “My Projects”. Click the link “Add New Project” in the left nav bar, to create a new project.

Screen Shot 2013-02-04 at 5.40.07 PM

Simply upload a project file. A pom.xml, Gemfile, composer.json, package.json, dependency.gradle or another supported file with your dependencies. After the upload you will see a tab with all the dependencies.

Screen Shot 2013-02-04 at 5.40.53 PM

Simply click on the “License” tab to see all licenses in this project. And here it is.

Screen Shot 2013-02-04 at 5.41.17 PM

In this example most Libraries are using the MIT License. Some of them are under “Ruby” license and for 1 Library VersionEye was not able to detect a license.

It’s that simple. Let me know if you have questions to this.

Capistrano + Rails + Unicorn + NGinx + RVM

Capistrano is a ruby based deployment tool which executes commands in parallel on multiple remote machines, via the SSH protocol. With Capistrano you can deploy your application on N server with one single command from your dev machine. You don’t need to login via SSH to your server.

Screen Shot 2013-01-15 at 8.15.33 PM

If you don’t deploy your app on Heroku or CloudControl, if you have dedicated servers or you are using AWS, than Capistrano is the way to go.

Before you start with Capistrano, you have to implement SSH with authentification keys instead of password. In that way you can just login to your server with a simple “ssh user@server” without password. That is possible if your public ssh certificates are on the server. In that way the server “knows” you.

First you need to add the Gem to your Gemfile.

gem 'capistrano'
gem 'rvm-capistrano'

I am adding here “rvm-capistraon”, too. Because I am working with RVM on my dev machine and on the server. Now you have to run bundler.

bundle install

As next step you have to capify your rails project. Just run:

capify .

That will create some files in your project.

[add] writing './Capfile'
[add] writing './config/deploy.rb'
[done] capified!

Your Capfile is loading the deploy.rb script. The Capfile should look like this:

load 'deploy'
# Uncomment if you are using Rails' asset pipeline
# load 'deploy/assets'
load 'config/deploy'

The magic happens in the deploy.rb file. The full documentation to this script you will find here: 
https://github.com/capistrano/capistrano
. It basically executes a couple of commands on your server. It fetches the current code from your GIT server, runs bundler, rake db:migrate, precompiles your assets and runs the ruby app server.

Here is my deploy.rb with some additional comments.


# Automatically precompile assets
load "deploy/assets"

# Execute "bundle install" after deploy, but only when really needed
require "bundler/capistrano"

# RVM integration
require "rvm/capistrano"

# Name of the application in scm (GIT)
set :application, "your_app"
set :repository, "git@github.com:your_company/your_project/project.git"

# Source Control Management
set :scm, :git

set :deploy_to, "/var/www/#{application}"

# server there the web server is running (nginx)
role :web, "192.168.0.12"

# server there the app server is running (unicorn)
role :app, "192.168.0.12"

# server there the db is running
# This is where Rails migrations will run
role :db, "192.168.0.12", :primary => true

set :rails_env, :production

# user on the server
set :user, "project_user"
set :use_sudo, false

# Target ruby version
set :rvm_ruby_string, '1.9.3'

# System-wide RVM installation
set :rvm_type, :system

# Apply default RVM version for the current account
after "deploy:setup", "deploy:set_rvm_version"

namespace :deploy do

  task :set_rvm_version, :roles => :app, :except => { :no_release => true } do
    run "source /etc/profile.d/rvm.sh && rvm use #{rvm_ruby_string} --default"
  end

  task :start, :roles => :app, :except => { :no_release => true } do
    run "/etc/init.d/unicorn start"
  end

  task :stop, :roles => :app, :except => { :no_release => true } do
    run "/etc/init.d/unicorn stop"
  end

  task :restart, :roles => :app, :except => { :no_release => true } do
    run "/etc/init.d/unicorn restart"
  end

  # Precompile assets
  namespace :assets do
    task :precompile, :roles => :web, :except => { :no_release => true } do
      run %Q{cd #{latest_release} && #{rake} RAILS_ENV=#{rails_env} #{asset_env} assets:precompile}
    end
  end

end

The script for starting and stoping unicorn you can find here: 
http://robert-reiz.com/2012/02/29/running-unicorn-as-a-service-on-debian-linux/
.

Now you can deploy with:

cap deploy

And this command shows you all possible Capistrano tasks:

cap -T

Let me know if you have questions.

Ruby on Rails + ElasticSearch

This is a tutorial how to use ElasticSearch with Ruby on Rails. ElasticSearch is a distributed RESTful Search Engine build on top of Apache Lucene.

Sure! You can use your SQL database for search. But that is usually slow and you will not get very good search results. With ElasticSearch you can deliver a fuzzy search. Let’s say you have a record “Hibernate” in the database. If somebody is doing a search for “hibernate” you will get a match with a simple SQL query. But what if your customers input looks like this:

  • hibernate 3.2
  • hibernate.jar
  • hibernate.jar 3.5

In this cases you will have 0 results with a simple SQL query. With ElasticSearch you would still have results. Depending on your configuration. So let’s start!

ES

Just download the current version from here: 
http://www.elasticsearch.org/download/
. Unpack it and run the ES server with this command:

./bin/elasticsearch -f

The ES server is now running on localhost:9200. If you type in “
http://localhost:9200/_search
” into your browser you should get some basic results.

I assume that you know already Ruby on Rails and ActiveRecord. Of course there is a GEM to interact with ElasticSearch. Checkout the Tire GEM. That is a pretty good wrapper for ElastisSearch. Just add it to your Gemfile.

gem 'tire', '0.5.4'

and run:

bundle update

In your application.rb you need to require the new package:

require 'tire'

And in environment.rb you need to init the package:


begin
  Tire.configure do
    logger STDERR
    url Settings.elasticsearch_url
  end
rescue => e
  p "Wrong configuration: #{e}"
end

On the GitHub Page there is a description how to integrate Tire into your model. But honestly I don’t like that very much. That just blows up the model class. I prefer a clear separation between my models and the interaction with ElasticSearch.

The model I wanted to map and to make searchable with ElastiSearch is “product.rb”. It is located in “app/models/”. I created another directory called “app/elastics/”. And here I placed a new file “product_elastic.rb”, which is mapping my Product class to ElasticSearch and is responsible for the interaction with the ES server.

The first thing you have to do is to create a mapping. You have to map your properties from your model to ElasticSearch. This is how I did my first mapping:


def self.create_index_with_mappings
  Tire.index Settings.elasticsearch_product_index do
    create :mappings => {
      :product => {
        :properties => {
          :_id => { :type => 'string', :analyzer => 'keyword', :include_in_all => false },
          :name => {:type => 'string', :analyzer => 'snowball', :boost => 100},
          :description => { :type => 'string', :analyzer => 'snowball' },
          :description_manual => { :type => 'string', :analyzer => 'snowball' },
          :language => { :type => 'string', :analyzer => 'keyword'}
        }
      }
    }
  end
 end

The analyzers are documented on the ElasticSearch homepage: 
http://www.elasticsearch.org/guide/
. Here is the magic happening ;-)

Than I wrote 2 more methods (clean_all and reset) to delete the “product” index at ES and to create the mappings.


def self.clean_all
  Tire.index( Settings.elasticsearch_product_index ).delete
end

def self.reset
  self.clean_all
  self.create_index_with_mappings
end

Don’t call the reset method in production ;-) With this 3 methods you can delete old mapping and create a new one. You can now run the rails console and try out the methods. That should all work fine.

If creating the mapping was successful, the next step is to index the data in the database. To index a model, the model itself must offer a method which returns their values as JSON. I added a “to_indexex_json” method to the Product class:


def to_indexed_json
 {
   :_id => self.id.to_s,
   :_type => "product",
   :name => self.name,
   :description => self.description ? self.description : "" ,
   :description_manual => self.description_manual ? self.description_manual : "" ,
   :language => self.language,
   :group_id => self.group_id ? self.group_id : "",
   :prod_key => self.prod_key,
 }
 end

The first 2 attributes are required by Tire, to link the response from ES with your model. And here is the method in “product_elastic.rb” to index one record.


def self.index( product )
  Tire.index Settings.elasticsearch_product_index do
    store product.to_indexed_json
    product.update_attribute(:reindex, false)
  end
rescue => e
  p "ERROR in index(product) Message: #{e.message}"
  p "ERROR in index(product) backtrace: #{e.backtrace}"
end

And this here is the method to index all products from the DB:


def self.index_all
  Product.all.each do |product|
    ProductElastic.index product
  end
  self.refresh
end

def self.refresh
  Tire.index( Settings.elasticsearch_product_index ).refresh
end

Easy! Right? You can try the methods in the rails console. All right. You can now create mappings and index data. The only thing which is missing now is the search method. There is a lot to say about the search. I really recommend that you take your time and read the documentation about the search at the Tire homepage and the ES homepage. But here is one example.


def self.search(q, page_count = 1)
  if (q.nil? || q.empty?)
    raise ArgumentError, "query is empty! This is not allowed"
  end

  page_count = 1 if page_count.nil? || page_count.to_i < 1
  results_per_page = 30
  from = results_per_page * (page_count.to_i - 1)

  q = "*" if !q || q.empty?

  s = Tire.search( Settings.elasticsearch_product_index,
   load: true,
   from: from,
   per_page: results_per_page,
   size: results_per_page) do |search|

    search.sort { by [{:_score => 'desc'}] }

    search.query do |query|
      query.string "name:" + q
    end

  end

  s.results
end

The method above is with paging. You can easily use it with the “will_paginate” GEM.

There is much more to say about the mapping and the search. But this is the part there you have to invest time to figure it out, to deliver the search results you want. You will find a pretty good documentation on the ElasticSearch homepage: 
http://www.elasticsearch.org/guide/
. And the core committer from the Tire project always responded in less than 24 hours to my tickets. The project support is very good.

Together with Timo I integrated ElasticSearch into VersionEye. Since them the search is much faster and the results are better. Even if there is no perfect match you will get results.

Installing native pg gem on Mountain Lion

I tried to install the native pg gem on Mac OS X Mountain Lion (10.8.2):

gem install pg

and got this exception:

Building native extensions. This could take a while...
ERROR: Error installing pg:
 ERROR: Failed to build gem native extension.
/Users/robertreiz/.rvm/rubies/ruby-1.9.3-p327/bin/ruby extconf.rb
checking for pg_config... yes
Using config values from /usr/bin/pg_config
checking for libpq-fe.h... yes
checking for libpq/libpq-fs.h... yes
checking for pg_config_manual.h... yes
checking for PQconnectdb() in -lpq... yes
checking for PQconnectionUsedPassword()... yes
checking for PQisthreadsafe()... yes
checking for PQprepare()... yes
checking for PQexecParams()... yes
checking for PQescapeString()... yes
checking for PQescapeStringConn()... yes
checking for PQescapeLiteral()... yes
checking for PQescapeIdentifier()... yes
checking for PQgetCancel()... yes
checking for lo_create()... yes
checking for pg_encoding_to_char()... yes
checking for pg_char_to_encoding()... yes
checking for PQsetClientEncoding()... yes
checking for PQlibVersion()... yes
checking for PQping()... yes
checking for rb_encdb_alias()... yes
checking for rb_enc_alias()... yes
checking for PGRES_COPY_BOTH in libpq-fe.h... no
checking for PGRES_SINGLE_TUPLE in libpq-fe.h... no
checking for struct pgNotify.extra in libpq-fe.h... yes
checking for unistd.h... yes
checking for ruby/st.h... yes
creating extconf.h
creating Makefile
make
compiling pg.c
pg.c: In function ‘Init_pg_ext’:
pg.c:384: error: ‘PQPING_OK’ undeclared (first use in this function)
pg.c:384: error: (Each undeclared identifier is reported only once
pg.c:384: error: for each function it appears in.)
pg.c:386: error: ‘PQPING_REJECT’ undeclared (first use in this function)
pg.c:388: error: ‘PQPING_NO_RESPONSE’ undeclared (first use in this function)
pg.c:390: error: ‘PQPING_NO_ATTEMPT’ undeclared (first use in this function)
make: *** [pg.o] Error 1

I could fix it by installing postgres via Homebrew.

brew update
brew install postgresql

and after that this here worked perfect:

gem install pg

Brew is awesome!

Reset Postgres DB on Heroku

In a Rails application you can you this tasks on localhost to reset the database.

rake db:drop
rake db:create
rake db:migrate

And that will delete the db schema, create a new one and run all migrations to create the tables. This is awesome!
But if try to run this on heroku:

heroku run rake db:drop

Than you will get an exception, because you don’t have permission to delete dbs on Heroku. If you want to reset your database on Heroku you have to use this command:

heroku pg:reset DATABASE_URL

this is equal to “rake db:drop” and “rake db:create”. After that you still have to run:

heroku run rake db:migrate

That worked pretty good for me.
Just wanted to share my experience.