Testing,Documentation and Others

Past two weeks, i have studied some useful gems in rails. Here are the summary and tips.

Cucumber

Cucumber is the Behavior Driven Development(BDD). Writing testing is the most important in software development. Without testing case it’s hard to refactor any code in the future.

For instance: my site provide REST API with JSON and require devise login to use. How to test?
Add to Gemfile. Suggest to use rspec and factory_gril gems together.

Gemfile
1
2
3
4
5
6
7
8
9
group :test do
  gem "rspec-rails"
  gem 'cucumber-rails', :require => false
  gem "factory_girl_rails"
  gem 'database_cleaner'
end
group :development do
  gem "rspec-rails"
end

Run rails g cucumber:install to initial features/ structure.

Modify config/initializers/devise.rb if you use devise and need to test sign_out function.

devise.rb
1
config.sign_out_via = Rails.env.test? ? :get : :delete

Create api.feature in features/. You should describe your feature by plain english.

api.feature
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
Feature: Provide API
  In order to show list
  As an Frontend javascript
  Should be able to retrieve data through REST API

  Scenario: list category through REST API
    Given I exist as a user and logged in
      And the system knows about the following category
       | 1 |  |
       | 2 | 交際娛樂 |
       | 3 | 交通 |
    When I send a GET request to category api
    Then should response correct category JSON data
      """
      {"monny":[{"cid":1,"name":""},
      {"cid":2,"name":"交際娛樂"},
      {"cid":3,"name":"交通"}]}
      """

Create api_steps.rb in features/step_definitions/ and users.rb in spec/factories/.

api_steps.rb
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
def create_user
  @user = FactoryGirl.create(:user)
end

def sign_in
  visit '/users/sign_in'
  fill_in "user_email", :with => @user[:email]
  fill_in "user_password", :with => @user[:password]
  click_button "Sign in"
end

Given /^I exist as a user and logged in$/ do
  create_user
  sign_in
  page.should have_content "Signed in successfully."
end

And /^the system knows about the following category$/ do |table|
  table.raw.each do |cid, name|
    Category.create({cid: cid, name: name})
  end
end

When /^I send a GET request to category api$/ do
  @response = page.driver.get api_category_path
end

Then /^should response correct category JSON data$/ do |expect|
  actual = JSON.parse(@response.body)
  expect = JSON.parse(expect)
  expect.each do |key,value|
    assert_equal value, actual[key]
  end
end
users.rb
1
2
3
4
5
6
7
FactoryGirl.define do
  factory :user do
    email 'a@a.com'
    password 'a1234567'
    confirmed_at Time.now
  end
end

Here are some items should be take care.
1. Use capybara to visit sign_in page and click it to sign in
2. With | name | yyy |, you can pass a table into steps function. I use it to create Category.
3. Use page.driver.get api_category_path to send GET request with authentication session(Because you have sign in)
Run cucumber to test and get report.

Ransack

Search your data in active record. I add the search function into rails101_groupme project. The usage is quite simple. The groupme site has a search field in the navigation bar to search all posts.
groupme Add a method in application_controller.rb because we have a search field in nav bar and every controller need to render it.

application_controller.rb
1
2
3
4
5
6
7
8
...
# exec set_search before_filter
before_filter :set_search

def set_search
  @q = Post.search(params[:q])
end
...

Add controller to handle search request.

search_controller.rb
1
2
3
4
5
class SearchController < ApplicationController
  def search
    @posts = @q.result(distinct: true)
  end
end

Add search path in route. Only accepts post here.

routes.rb
1
2
3
...
post 'search' => 'search#search'
...

Add search view. Ransack has search_form_for tag and it’s nice!

search.html.erb
1
2
3
<%= search_form_for @q, :class => "navbar-form navbar-left", url: search_path, html: { method: :post }  do |f| %>
  <%= f.text_field :content_or_title_cont %>
<% end %>

:content_or_title_cont means we want to search content or title that matches search keyword. If you want to search title column only, you should use :title_cont. _cont is necessary and means column’s content. column name is the same with the active record you defined.

Yard

Documentation is important. The Yard syntax is like Java and i like it.

1
2
3
4
5
6
7
8
9
10
11
12
# @author weichienhung
# Get places data belong to the category.  
# It uses _id_ from ApplicationController#params to query Place from Category.
# @return [JSON] the Place json format.
def show
  begin
    @category = Category.find(params[:id])
    respond_with @category.places
  rescue
    respond_with []
  end
end

Yard supports markdown syntax in describe your method. _id_ is the italic word in markdown.

Comments