Archive

Tag Archives: Rails

I’ve spent some time today changing the Cucumber/Capybara tests in one of my pet projects to use a page model. Since I didn’t find much stuff on the interwebs about it, why not write it here ?

The idea behind having a page model is to keep steps related to a specific page on your app in the same place, so you can reduce the repetition of steps in different tests. Is definitely not a complicated practice, and setting Capybara for it is a simple step.

Setting the context, Im using Cucumber 0.10 with Capybara 0.4.1.2 and Rails 3.0.1. Have done the standard installation steps recommended by the cucumber-rails github page.

The only modification I’ve made with the created structure is adding a folder for the page objects, so the final structure is this:

As you can see, inside the pages folder there is a home page file, which is responsible for every action/assertion related to the home page.

Nothing new with the cucumber features, which keep having it’s standard style

Feature: Manage tasks
In order to manage my tasks,
a user
wants to create tasks in different categories

Scenario: Create a new task
Given I am in the Do Me home page
When I create an urgent and important task with description "my task"
Then I should see "my task" in the "Urgent and Important" section

However, in order to create our page object, we need to inject the test driver on it, which in Capybara's case, is the session object.

Given /^I am in the home page$/ do
@home_page = HomePage.new(Capybara.current_session)
@home_page.visit
end

When /^I create an urgent and important task with description "([^"]*)"$/ do |task_description|
@home_page.fill_task_description(task_description)
@home_page.check_important
@home_page.check_urgent
@home_page.create_task
end

And from there is just the trouble of creating the page class (or do like me, who shamelessly copied the style from here).


class HomePage
</code


URL = "/"

def initialize(session)
@session = session
end

def visit
@session.visit URL
end

def fill_task_description(description)
@session.fill_in("Description", :with => description)
end

def check_urgent
check("Urgent")
end

def check_important
check("Important")
end

def create_task
@session.click_button("Create Task")
end

And that's pretty much it, now is just choosing your preferred driver and run the tests. As you can see, not much effort for a nice improvement.

In the set of new features from Rails 2.3, I was quite pleased by two of them: nested attributes and nested forms.

Both allow the creation of forms containing information about more than just one model, and may solve a recurrent problem I had in past projects, which has lead to some painful headaches…

Can’t wait to try it!

More information (source):

3.1. Nested Attributes

Active Record can now update the attributes on nested models directly, provided you tell it to do so:

class Book < ActiveRecord::Base
  has_one :author
  has_many :pages

  accepts_nested_attributes_for :author, :pages
end

5.1. Nested Object Forms

Provided the parent model accepts nested attributes for the child objects (as discussed in the Active Record section), you can create nested forms using form_for and field_for. These forms can be nested arbitrarily deep, allowing you to edit complex object hierarchies on a single view without excessive code. For example, given this model:

class Customer < ActiveRecord::Base
  has_many :orders

  accepts_nested_attributes_for :orders, :allow_destroy => true
end

You can write this view in Rails 2.3:

<% form_for @customer do |customer_form| %>
  <div>
    <%= customer_form.label :name, 'Customer Name:' %>
    <%= customer_form.text_field :name %>
  </div>

  <!-- Here we call fields_for on the customer_form
       builder instance. The block is called for each
       member of the orders collection. -->
  <% customer_form.fields_for :orders do |order_form| %>
      <p>
        <div>
          <%= order_form.label :number, 'Order Number:' %>
          <%= order_form.text_field :number %>
        </div>

  <!-- The allow_destroy option in the model
       enables deletion of child records. -->
        <% unless order_form.object.new_record? %>
          <div>
            <%= order_form.label :_delete, 'Remove:' %>
            <%= order_form.check_box :_delete %>
          </div>
        <% end %>
      </p>
    <% end %>
  <% end %>

  <%= customer_form.submit %>
<% end %>
Follow

Get every new post delivered to your Inbox.

Join 836 other followers