Sunday 31 August 2014

Simple Step by Step testing in Ruby on Rails Using RSpec

Straight to the point.
open up terminal and create a new Rails project, simple_test:

    $ rails new simple_test --skip-test-unit
    $ cd simple_test

option --skip-test-unit, tells Rails not to generate a test directory associated with Test::Unit framework. this is because we want to use another testing framework named RSpec to write our tests.

then we need to include RSpec Gem in 'Gemfile' in your project root directory

    group :development, :test do
      gem 'sqlite3', '1.3.8'
      gem 'rspec-rails', '2.13.1'
    end

    group :test do
      gem 'selenium-webdriver', '2.35.1'
      gem 'capybara', '2.1.0'
    end
  
This includes rspec-rails in a development environment so that we have access to RSpec-specific generators, and it includes it in test mode in order to run the tests. We don’t have to install RSpec itself because it is a dependency of rspec-rails and will thus be installed automatically. We also include the Capybara gem, which allows us to simulate a user’s interaction with the sample application using a natural English-like syntax, together with Selenium, one of Capybara’s dependencies.

    $ bundle install

it might be better to update the gems with bundle update to make sure the versions match, and then run bundle install to make sure the Gemfile.lock file is fully up-to-date.
Next, we need to configure Rails to use RSpec in place of Test::Unit.

    $rails generate rspec:install

you should see:

    create  .rspec
    create  spec
    create  spec/spec_helper.rb
if you're using sublimeText, you can open the entire project by navigating to your project directory and then

    $ subl .

or any other editor.
ok, now we set to start developing. since we're following MVC pattern here, I jut give an short overview about that. model is your data access layer. view is your presentation layer, and controller is your business logic layer. business layer i.e. controllers, are between those 2 other layers.
it means, if some data needs to be shown by presentation layer, i.e. views, it can't access the model (data access layer, or database side) by itself. it needs to go through the business layer i.e. controllers.
now if you opened the entire project in your editor, and then go into 'app' folder in project root directory you'll see folders related to these layers, models, views, controller. there are some convention here which makes the development easier, which you can search it for yourself.
now lets make a controller:

    rails generate controller StaticPages home help --no-test-framework

--no-test-framework option suppresses the generation of the default RSpec tests, which we won’t be using. Instead, we’ll create the tests by hand. this will generates a controller, StaticPages, with 2 action methods : home and help, which both represent 2 pages(look into app/view/static_pages). now, since i just want to show how TDD works, i don't go into details on how writing tests for the existing code. instead suppose i want to add another page, About.
In test-driven development, we first write a failing test, represented in many testing tools by the color red. We then implement code to get the test to pass, represented by the color green. Finally, if necessary, we refactor the code, changing its form (by eliminating duplication, for example) without changing its function. This cycle is known as “Red, Green, Refactor”.
because here, i'll do integration testing we need to run following command:

    $ rails generate integration_test static_pages
      invoke  rspec
      create    spec/requests/static_pages_spec.rb

This creates the static_pages_spec.rb in the spec/requests directory. As with most generated code to use the Capybara you need to add the following line in  spec/spec_helper.rb

    .
    .
    .
    RSpec.configure do |config|
      .
      .
      .
      config.include Capybara::DSL
    end

checkpoint: now we have a controller, static_pages_controller, it has 2 action methods, home, help and we want to add another one, about. we have RSpec installed, and we execute the rails generate integration_test static_pages, which generated spec/requests/static_pages_spec.rb for us. we put our test inside that file.

Actual TDD

We’ll start the testing cycle by writing a failing test for the About page.

    in spec/requests/static_pages_spec.rb

    require 'spec_helper'

    describe "Static pages" do
      describe "About page" do

        it "should have the content 'About Us'" do
          visit '/static_pages/about'
          expect(page).to have_content('About Us')
        end
      end
     end

if you run the RSpec using

    $ bundle exec rspec spec/requests/static_pages_spec.rb

the output is very informative. it includes:

    No route matches [GET] "/static_pages/about"

this means we need to add /static_pages/about to routes file in config/routes.rb

    get "static_pages/about"

now running the test again

    $ bundle exec rspec spec/requests/static_pages_spec.rb

complains that

    The action 'about' could not be found for StaticPagesController

go to app/controllers/static_pages_controller.rb and add action for about, same as home and help actions
      
    def about
    end

now running the test again

    $ bundle exec rspec spec/requests/static_pages_spec.rb

says that we are missing a “template”, i.e., a view:

    Missing template static_pages/about

To solve this issue, we add the about view. This involves creating a new file called about.html.erb in the app/views/static_pages directory with following content:   

<h1>About Us </h1>

Running the test again

    $ bundle exec rspec spec/requests/static_pages_spec.rb

it should show us the green.
to see the page you created, run the rails server in terminal:

    $rails server

then in your browser navigate to

    0.0.0.0:3000/static_pages/about

and you should see the about view you created.
now suppose we want to write test to check the page title. we want the "About page" has the title "About Us". we write the test first:

    it "should have the title 'About Us'" do
      visit '/static_pages/about'
      expect(page).to have_title("About Us")
    end

if you run the test

    $ bundle exec rspec spec/requests/static_pages_spec.rb

it should fail

    expected #has_title?("About Us")

The test uses the *have_title* method, which checks for an HTML title with the given content. In other words, the code

    expect(page).to have_title("About Us")

to pass the test you just need to change the title of about page to "Abut Us" in its relevant view file in app/views folder, which is easy

some side notes: 
these tests are technically integration tests, however writing unit tests are similar. you just test the data based on your model, for example for invalid data, or password length. etc.
  • I tried to be more detailed and descriptive, sorry if that's so long.
  • all the steps have been tested on Kubuntu 14.04
  • this was adopted mostly from http://www.railstutorial.org/
  • if you see any errors, leave a comment please 

No comments:

Post a Comment