codedecoder

breaking into the unknown…


1 Comment

jQuery Slider drag drop with Capybara

We have used jQuery Slider in one of our application, which resize the images within our library when user move it left and right. Since the no of images we can show per page, varies as user moves the slider, we have hooked a ajax call to stop event of slider which reset the per_page value in controller.

The code look as below:

JS code:

    var setupSlider = function () {

        var sl_value = $.cookie("sl_value");
        if (sl_value === undefined) {
            savedSliderValue = 0;
        } else {
            savedSliderValue = parseInt(sl_value);
        }

        $(".size-slider .slider").slider({
            range: "min",
            value: savedSliderValue,
            min: -50,
            max: 0,
            slide: function (event, ui) {
                var fraction = (1 + ui.value / 100),
                        newWidth = originalWidth * fraction;

                $(".slide-library .slide").width(newWidth);
            },
            stop: function (event, ui) {
                var sl_value = $(".size-slider .slider").slider("option", "value");
                $.cookie("sl_value", sl_value);
                var expansion_factor = (0 - ui.value);
                $.ajax({
                    type: "GET",
                    url: 'desktop.js',
                    data: {
                        expansion_factor: expansion_factor
                    },
                    contentType: 'script'
                });
           }
     });

Controller Logic look as Below:

    #the slide_per_page will change on slide resizing. default is 12
    cookies[:slide_per_page] ||= 12
    cookies[:slide_per_page] = params[:expansion_factor].present? ? (12 + (12 * params[:expansion_factor].to_i/50)) : cookies[:slide_per_page]
    #reset page number to 1 as library resizing reinit the pagination
    page_number = params[:expansion_factor].present? ? 1 : current_page
    @library_slides =  Slide.all.paginate(:page => page_number, :per_page => cookies[:slide_per_page])

 

Now the test case, need to simulate the drag drop behaviour of slider.

Problem :

We are using Capybara which do give drag_to method to simulate drag and drop, but it take argument as source and target i,e you can drop one element on other element, But here there is no such distinction for the Slider as User can drag it anywhere on slider length, left or right.

Solution:

Capybara internally use Selenium and it have drag_and_drop_by method which simulate drag and drop as offset i,e as X and Y coordinate . So we will extend Capybara to have our custom method.

Well we know the solution, so lets move ahead. Now the question is where to put our code, so that Capybara pick it up and is then available in our Rspec test cases.

All Rspec configuration is loaded from spec_helper.rb file whenever, you run your Rspec tests. My spec_helper.rb file look as below

require 'database_cleaner'
require 'factory_girl_rails'
require 'support/capybara'
require 'warden'
require 'devise'

RSpec.configure do |config|
  config.include Capybara::DSL
  #many more line of configuration...
end

So you can see that in the third line we have required ‘support/capybara’ (complete path – spec/csupport/capybara) , this file contain capybara specific configuration and initialize Capybara whenever Rspec is run. We will add our extension code at bottom of this file

support/capybara

module CapybaraExtension
  def drag_by(right_by, down_by)
    base.drag_by(right_by, down_by)
  end
end

module CapybaraSeleniumExtension
  def drag_by(right_by, down_by)
    driver.browser.action.drag_and_drop_by(native, right_by, down_by).perform
  end
end

::Capybara::Selenium::Node.send :include, CapybaraSeleniumExtension
::Capybara::Node::Element.send :include, CapybaraExtension

So now we have a drag_by method which perfom drag and drop on element with offset.Using it we can now simulate the jQuery Slider behaviour. Finally The Rspec test case look as below

  it "it should reinit the pagination on slide resizing" do
    visit slides_desktop_path(@project)
    #pagination set to 12 and we have around 33 records so including 2,3 and next there should be 3 link. 
    #Note that in pagination previous and 1 is default and so disabled when page loads
    expect(find("div.pagination")).to have_selector(".next_page", :count => 1)
    expect(find("div.pagination")).to have_selector(".previous_page", :count => 1)
    expect(find("div.pagination")).to have_selector("em", :count => 1)
    expect(find("div.pagination em.current").text).to eq("1")
    expect(find("div.pagination")).to have_selector("a", :count => 3)
    first(".ui-slider-handle").drag_by(-50, 0)
    sleep 2
    expect(find("div.pagination")).to have_selector("a", :count => 2)
    first(".ui-slider-handle").drag_by(50, 0)
    sleep 2
    expect(find("div.pagination")).to have_selector("a", :count => 3)
  end

That’s all. Run your test and see it passing ūüôā


3 Comments

undefined method `visit’ for rspec capybara

Recently, I am trying to integrate rspec and capybara with sinatra (see here). But when I try to run my test file, I get below error

NoMethodError:
undefined method `visit’ for #<RSpec::Core::ExampleGroup::Nested_1:0x9209d5c @example=nil>

I find it strange, as I have configured capybara many a time with rails and it work smoothly. I get some hint of problem here on stack overflow.

visit is a method defined by capybara over rspec layer i,e it is DSL (domain specific language). when working with rails, capybara DSL module is automatically loaded by rails when rspec is loaded, but with sinatra, this is not happening. So we have to include  Capybara::DSL in the file we want to use its method. I have added the include line in my spec_helper.rb file which is required in my all other test files.

SOLUTION : add include Capybara::DSL to your configuration file or the individual test file

My, spec_helper.rb file look like this

require 'capybara'
include Capybara::DSL # Adding this line solved the error
Capybara.default_driver = :selenium

def app 
  Sinatra::Application 
end 

set :environment, :test 
RSpec.configure do |config| 
  config.treat_symbols_as_metadata_keys_with_true_values = true 
  config.run_all_when_everything_filtered = true 
  config.filter_run :focus config.order = 'random' 
end


Leave a comment

Rails metrics : load, performance and code quality

Sometime, we need some evaluation of our code to see its robustness, performance, quality etc. These can be gather into a metrics, we will call it Rails Metrics. Fortunately, gems are available to provide all these metrics. A good detail of available metrics can be found here . I personally, feel that at start of your project, we should also set up tools to provide us with metrics under below category.

Metric 1: Code Quality

This will track our code quality in terms of best practice. Best Practices can be found on this site.  it will show error in the metrics when we deviate from the best practices. The rails_best_practices gem can be used for this.

Metric 2: Code test coverage.

This metric will tell you, how much of your code is covered under different test cases and how much is left out. You can then add test cases for those piece of code. A no of tools are available for this on ruby tool box. The gem I preferred is simplecov . If you are using below ruby 1.9.x, you can also use rcov. rcov  do not support ruby 1.9.x.

Metric 3:  Code Performance

It will provide metrics about, your active record calls, method replication, query optimisation etc. newrelic provide performance testing service. Though, it is paid service. In Development mode you can use it for free. The newrelic rpm gem is available here

Metric 4: load metrics

This metric is generally needed for production. I have discussed in detail about various tools available for this in this post

NOTE : There is a no of other metrics also available. metric_fu is the gem which integrate a no of other tool metrics like Flog, Flay, RCov, Saikuro, Churn, Reek, Roodi etc.  So , You can install it directly to get all the metrics at once or keep installing other metric gem as per your need.


Leave a comment

skip test with rspec default run

We have adopted build process for our application. It run the rspec test, every time we push any code to git master branch and send emails to listed developers if the rspec fails. The error email become frequent due to failures of 3 spec written to test openam sso functionality. Those three tests are failing due to openam API problem, say the openam server is down or so on. Since, we do not have control over that, it is decided to skip these test with rspec default run.

Rspec provides tag feature to tag specs. You can do it at each spec level or context level or at the describe block. you can find more detail at rspec core documentation here

I have described my tag as below at context level

describe Users::OmniauthCallbacksController do
¬†¬† describe “GET ‘saml'” do
¬†¬†¬†¬†¬†¬† context “openam authentication”,¬†:api_openam => true do

¬†¬†¬†¬†¬†¬†¬†¬†¬† it “user should be able to signin with openam” do
¬†¬†¬†¬†¬†¬†¬†¬†¬†¬† ….write the test…
          end

¬†¬†¬†¬†¬†¬†¬†¬†¬† it “user should be able to logout with openam” do
¬†¬†¬†¬†¬†¬†¬†¬†¬† ….write the test…
         end

¬†¬†¬†¬†¬†¬†¬†¬† it “openam logout should destroy local session also” do
¬†¬†¬†¬†¬†¬†¬†¬†¬† ….write the test…
        end
     end
    end
end

You can attach a tag, similarly at each spec level as below

it “openam logout should destroy local session also”,¬† :api_openam => true do
….write the test…
end

Also. the tag is basically have name => value syntex i,e it is not necessary to put true, if some thing else suits better say you want value to be api, then you can write¬†:api_openam => “api” instead of¬†:api_openam => true

The difference, is the way you can use this tag to run the rspec

To run the test which contain¬†api_openam tag, you will just pass –tag¬†api_openam option as below

$ rspec spec/controllers/users/omniauth_callbacks_controller_spec.rb –tag openam
Run options: include {:api_openam=>true} #thus the test is run which  contain the api_openam tag

To run the test which do not contain¬†api_openam tag, you will just pass –tag ~api_openam option as below

$ rspec spec/controllers/users/omniauth_callbacks_controller_spec.rb –tag ~openam
Run options: excludee {:api_openam=>true} #thus the test is run which do not contain the api_openam tag

So, this is the basic of how we can run a rspec from console with tag or without tag which will filter your spec accordingly. but I need to skip some spec or test by default whenever rspec is run. This can be handle with rspec configuration in spec_helper.rb file. detail of all the available rspec configuration is available here

What we need for our case is do the configuration for filter_run_excluding. I have added it to spec_helper.rb with below line

config.filter_run_excluding :api_openam => true

Now when you run rspec with default i,e without any options, it will exclude the test having openam tag

$ rspec
Run options: exclude {:api_openam=>true} #thus the test is run which do not contain the api_openam tag


Leave a comment

load testing with rails

One of our project is extensively based on API calls. The API service providers have there own bottleneck and take a lot of time to process our request and send the response. The overall page load time is not encouraging. The client wanted to test the robustness  of application and to see the overall response time when the number of concurrent user increases. Here, come the need to do load testing for the application. I tried to evaluate the tools available in the market. I came across below options.

Ruby Gems

being ruby developer, first I tried to find out some tools on ruby community like ruby toolbox and rubygems . I found some options at this link.

I give a try to two of the gems tourbus and trample. ¬†They didn’t suits my need. I looked into other gems also and felt below

-> There is still not any decent gem available on rails community to do a average load testing

-> Most of the gem owners have left development and maintenance of the gems they started building

-> None, of the gems have a proper documentation or a demonstrable examples

-> The two gems I have tried have problem in there installation itself. Strange thing is that tourbus has fixed the reported installation issue with ruby 1.9.3 and above 4 months back, but still not pushed the code to rubygem.org. Since bundle install gems from rubygems.org, you get the erroneous code which will not work with ruby 1.9.3. You can bypass the default by passing the git repository as source and get the current code but then it will get into some other trouble. I decided to better let it go ¬†ūüôā

Opensource Tools

Surfing for free load testing tools , I come across below options. jmeter look best to me among them. jmeter have excellent documentation

->Pylot

-> Apache Jmeter

-> Tsung

-> Siege

Paid Load Testing Tools

I¬†search¬†for some paid tool also. Being paid, they are far better then other available options, at least I¬†felt¬†so ūüôā

-> Neotys

They are providing free trial for one month. I installed it to give a try and find it amazing. It is easy to use with proper documentation and video tutorial¬†. They also provide 24×7 technical support to help you out of any problem. Since, they have detailed¬†explanation of how to, I managed it myself without any problem. I suits all my needs.

-> Load runner 


Leave a comment

mocking with rspec

Recently, I felt the urgent need of mocking one of user method which make some API call, as capybara driver throwing connection reset error whenever, the method which trigger the APi call is invoked in a controller action. I mocked the method with below line of code.

@user.should_receive(:refresh_docs_from_loan_path).and_return(‚Äúanything‚ÄĚ)

This line, will see that when a controller action is called, it should call the refresh_docs_from_loan_path method of User model and return any thing as a string. It solved my problem, So I tried to learn more about mocking and gone through some useful links as below

-> http://rspec.rubyforge.org/rspec/1.2.7/classes/Spec/Mocks.html #provide detailed documentation on mocking

-> http://www.martinfowler.com/articles/mocksArentStubs.html # it tell the difference between mocking and stubing.

In my above example, @user is my mocked object, which I created using factory girl. but we can also create it with mock class itself as below

mock(name, options={})


Leave a comment

the connection was reset capybara

While running one of my¬† spec (loan_documents_controller_spec.rb ) , I’ am getting below error in the capybara driver , with all the spec which make a call to index action getting failed.

the connection was reset
the connection was reset when the page is loading

Thus, capybara show me above error, instead of loading the index page. Since the page is not being loaded, all test I have written for it is getting failed. On closer look to my action, I do find that, I’ am making API call within the action. So when that action is called, The API call get triggered and capybara failed to handle that, thus throwing the error. My controller code is as below.

class LoanDocumentsController < ApplicationController
    def index
        current_user.refresh_docs_from_loan_path
       @signed_documents_count = LoanDocument.signed(current_user.id).count
       @unsigned_documents_count = LoanDocument.unsigned(current_user.id).count
     end
end

I have defined refresh_docs_from_loan_path in my user model which is making the API call as below

class User < ActiveRecord::Base
     def refresh_docs_from_loan_path
           uri, payload = create_payload
¬†¬†¬†¬†¬†¬†¬†¬†¬†¬† response = RestClient.post uri, payload, :content_type => “application/xml”¬† # this is the line making the API call
     end
end

The solution is either figure out the way for capybara to handle the API call or mock the method which is making the API call. I opted for the second one, infact I do not know how to handle the first one ūüôā

the single line of code which did the stubing of the refresh_docs_from_loan_path of user is as below :

@user.should_receive(:refresh_docs_from_loan_path).and_return(“anything”)

You, may return whatever you want in place of “anything” which suites your need. Here, the @user is my signed in user for spec which corresponds to the current_user in my controller. The complete sample code with one test is as below.

require ‘spec_helper’

describe LoanDocumentsController do
before(:each) do
@user = FactoryGirl.create(:user)
login_as @user # check it here how to login user in spec controller
end

¬†¬†¬† describe “GET ‘index'” do
¬†¬†¬†¬†¬†¬†¬† it “should load the index page if user logged in” do
¬†¬†¬†¬†¬†¬† ¬† ¬† ¬† @user.should_receive(:refresh_docs_from_loan_path).and_return(“anything”)
             visit loan_documents_path
¬†¬†¬†¬†¬† ¬† ¬† ¬† ¬† page.should have_content(“Document Status”)
         end
    end
end

We careful to add the mocked line just before capybara visit the action, here I have added just before visit loan_documents_path line. If you do as below

¬†@user.should_receive(:refresh_docs_from_loan_path).and_return(“anything”)
visit root_path
visit loan_documents_path

You will get below error

¬†Failure/Error: @user.should_receive(:refresh_docs_from_loan_path).and_return(“anything”)
(#<User:0xb6c2c70>).refresh_docs_from_loan_path(any args)
  expected: 1 time
received: 0 times

This is because, the action at root_path do not call the method refresh_docs_from_loan_path which is expected, so you get the error. One thing, I also observed that the mocked line work only with visit not with redirect_to . The below code will also throw the above error

¬†@user.should_receive(:refresh_docs_from_loan_path).and_return(“anything”)
redirect_to loan_documents_path