Programming
Testing a Rails application
May 25, 2015
0

Testing a Rails application

A personal approach

testing a rails applicationTesting a Rails application is often seen as complicated task and, when I started working with Ruby on Rails, I didn’t have a lot of experience with  TDD (just a little bit using Java in another company, but that was not real TDD because first functions where developed and the the tests) and in my first 2 years using RoR we were in such an hurry that TDD was seen as a nuisance.

What I discovered after a while was:

  • Poor object design because of rush
  • Error prone (no tests were developed)
  • A lot of time was lost  in bug fixing.

So I decided to stop and rethink my own development methodology (we’re still always in a hurry….) adopting TDD and I faced my first hurdle:

Q: Which are the best tools for RoR TDD?

A: After some googling I had a lot of answers, rspec, fixture, factories, rails default stuff and that confused me more, so I understood that the problem wasn’t the answer but the question; so I changed it in:

Q: Which are the most suitable tools for RoR TDD in my actual project?

A: many. Many different tools are suitable depending on what you need at a certain moment; sometimes you need fixtures and sometimes is better using factories. So I ended up using (extract from my Gemfile):

group :development, :test do
  gem 'railroady'
  gem 'rspec'
  gem 'rspec-rails', '~> 3.0.0.beta'
  gem 'factory_girl_rails', "~> 4.0"
end

group :test do
  gem 'faker'
  gem 'guard-rspec'
  gem 'launchy'
  gem "database_cleaner", :group => :test
  gem "email_spec", :group => :test
  gem "cucumber-rails", :group => :test, :require => false
  gem "capybara", :group => :test
  gem "figaro", :group => :test
  gem 'spork', '~> 0.9.0.rc'
  gem 'launchy'
  gem 'selenium'
  gem 'selenium-client'
  gem 'selenium-webdriver'
  gem 'nokogiri' # used to parse HTML output form QUnit tests
end

I’m not going to explain why I chose these gems; the only thing I can say is that the RSpec approach is really simple to set up and use quickly. Another thing I learned is that if you need to test controllers methods and you need a logged user, don’t use fixtures but use factories e.g:

Scenario: my application uses Devise for authentication and declarative authorization for roles:

  1. Create a factory for user, role
  2. in your personal_controller_spec.rb, before writing your tests add:
    before(:each) do
        @role =  FactoryGirl.create(:role) 
        @user =  FactoryGirl.create(:user, :roles => [@role]) 	
        @request.env["devise.mapping"] = Devise.mappings[:user]
        @request.session['whatever'] = @whatever.id
        sign_in @user
    end
    

    in this way, before every test rspec will create a logged in user with the role you need. Why I said to use factories and not fixtures? If, for example, you need to change the role, you can create an Hash with the new role attributes and pass it to FactoryGirl.create overriding the default values.

Another useful thing is the usual “keep it simple”, small tests for basic functionalities. I usually tend to do all the “dirty job” in the models, to keep controllers simple; this leads to unit tests with a lot of simple assertions and not so big and complicated functional tests.

in my opinion the most difficult thing to test are interface functionalities, you can use Capybara to run dry tests or you can combine it with something like Selenium web driver that will open a Firefox window and run the test for you. This is a nice approach but can be tough if you need a logged user for some functionalities, so here is a tip:

    1. In your spec folder create a folder called support
    2. create a file called feature_helpers.rb
    3. Add the following code to it:
      include Warden::Test::Helpers
      
      module FeatureHelpers
      	def login
      		@role =  FactoryGirl.create(:role) 
      		@user =  FactoryGirl.create(:user, :roles => [@role]) 	
      	login_as @user, scope: :user
          	@user
      	end
      
      	def inject_session(hash)
      		Warden.on_next_request do |proxy|
      			hash.each do |key,value|
      				proxy.raw_session[key] = value
      			end
      		end
      	end
      end
    4. Now open your rails_helper.rb and add this after all the ‘require’ and before the RSpec.configure
      Dir[Rails.root.join("spec/support/**/*.rb")].each { |f| require f }

      This will add the support folder to what the environment will load during the tests. In the RSpec.configure section add the following lines:

      config.include Devise::TestHelpers, :type => :controller
      config.include Authorization::TestHelper, :type => :controller
      config.include Warden::Test::Helpers, :type => :controller
      config.fixture_path = "#{::Rails.root}/spec/fixtures"
      config.include FeatureHelpers, type: :feature, :type => :feature

      These lines of code will load all the helpers needed for the authorization process.

    5. At this point you have to use these helpers in your features tests in this way:
      scenario "user goes to index page", :js => true do
      		login 
      		inject_session :whatever => @whatever.id
      		visit "/my_controllers/"
      		expect(page).to have_content('whatever')
      end

      This test will open a Firefox window (:js => true) simulate a login and put in the current session whatever you want using the inject_session helper and test if the page has the ‘whatever’ content.

So far this is what I’ve used to test my current project and in my opinion is the simplest possible approach. What I really think is that a golden rule doesn’t exist and you have to find what best fits your needs trying different ways of doing it.

Enjoy!

 

Leave a Reply

By continuing to use the site, you agree to the use of cookies. more information

The cookie settings on this website are set to "allow cookies" to give you the best browsing experience possible. If you continue to use this website without changing your cookie settings or you click "Accept" below then you are consenting to this.

Close