filter.js – Client side search filtering using JSON and jQuery

Speed for search result filtering is critical. Its fine for site users to wait for some time (maybe a few seconds) to load the search results but after that filtering better be fast otherwise people lose interest.

To give a simple example of how things are not user friendly is if you go to ebay.com or ebay.in. Its frankly appalling! For every click and every selection, the entire page refreshes. Though its good for showing Ad impressions (which I hate) when browsing an e-commerce site, the usability is lost.

On the other hand, have a look at snapdeal.com or groupon.com and it tells a different story. They fire Apis to their server to get json data and render that via Javascript. Usability is excellent. (no wonder they are doing so well!)

We did client-side search filtering in a couple of portals we built and realized that we should generalize this. That got us started and we ended up with filter.js This is a Javascript module that we have tried and tested on various portals.

Some key features are:

  • Category and sub-category based filtering
  • Slider based filtering
  • Trigger on any HTML element.
  • Date filtering (in process)

To use filter.js is simple. Add this to your <head> section:

<script src="https://raw.github.com/jiren/filter.js/master/filter.js" type="text/javascript"></script>

There are dependencies on how to render HTML components. Currently, its our own custom code but we have plans to upgrade this to integrate  with mustache or handlebars. For now, we live with it ;)

Defining the search criteria and the JSON objects

You can generate JSON data in any way you wish. Remember that the top-level entity will be used for rendering the view – in this case ‘person’.

var people = [{person: {name: 'Jiren', age:26, country: 'India', country_id: 1,
                        states : [{ state : 'MH', state_id : 3 },
                                  {state : 'HN', state_id : 4}] } },
              {person: {name: 'Joe', age:25, country: 'USA', country_id: 2,
                        states : [{ state : 'MH', state_id : 3 },
                                  {state : 'HN', state_id : 4}] } }
             ]

Defining the view function

We have some custom code for rendering HTML components. This assumes that you have the json objects in place. Here is an exmaple:

var view = function(person){
    name = this.span({'class': 'name'}, person.name);
    age = this.span({'class': 'age'}, person.age);
    country = this.div({'class': 'country'}, person.country);
    return this.link('/demo/' + person.id ,{'title': person.name}, [name,age,country]);
};

A more detailed example is available in the examples given on github. To define the filter criteria, all you need to do is pass a combination of the HTML components, the events and the JSON object attributes. For example:

var settings = {
  filter_criteria: {
    country: ['#country_list input:checkbox .EVENT.click .SELECT.:checked', 'country_id'],
    age: ['#age_list input:checkbox .EVENT.click .SELECT.:checked .TYPE.range', 'age'],
    price: ['#price_filter .EVENT.change .SELECT.:input .TYPE.range', 'timeleft']
    states: ['#state_list input:checkbox .EVENT.click .SELECT.:checked', 'states.ARRAY.state_id'],
  }
};

/* Trigger the filtering */
var fJS = new filterJS(people, "#people_list", view, settings);

Here is how this works:

Category based filtering

['#country_list input:checkbox .EVENT.click .SELECT.:checked', 'country_id']

All the checkboxes under the <div id=country_list> are used as the selectors. When the checkbox click event is triggered for any checkbox, all the checked check-boxes are used as the filter criteria and the JSON objects which match the ‘country_id‘ will be rendered using the view function. Rendering is done only once and then the display is toggled as needed.

Range based filtering via checkbox

age: ['#age_list input:checkbox .EVENT.click .SELECT.:checked .TYPE.range', 'age']

Its exactly the same as shown earlier except for the  additional .TYPE.range attribute. This tells the filter.js that it needs to pick up the range from the HTML element for filtering. This is typically added like this:

<div id='age_list'>
  ...
  <input type='checkbox' value="below-30">
  <input type='checkbox' value="30-50">
  <input type='checkbox' value="50-above">
...
</div>

So, now the filtering will trigger on the the range of age – this can be customized as you wish. Note the boundary limiters ‘below-30′ and ’50-above’.

Range based filtering via slider

price: ['#price_filter .EVENT.change .SELECT.:input .TYPE.range', 'timeleft']

Here the slider id change event triggers the filter and the value of the slider is used to filter the JSON data. Here is the sample HTML code. Ensure that you have jquery-ui.js included in the header.

<script type="text/javascript">
  $( "#price_slider" ).slider({
     range:true,
     min: 0,
     max: 1000,
     values:[0, 500],
     step: 5,
     slide: function( event, ui ) {
       $( "#price_range_label" ).html('$' + ui.values[ 0 ] + ' - $' + ui.values[ 1 ] );
       $('#price_filter').val(ui.values[0] + '-' + ui.values[1]);
       $('#price_filter').trigger('change');
     }
  });
</script>
<span id="price_range_label">$0-$100</span>
<div id="price_slider"></div>
<input type="hidden" id="price_filter"/>

Demo Time

Have a look at http://www.goodinkind.com/services and http://www.goodinkind.com/nonprofits to see filter.js in action in 2 different scenarios!

Alternatively, you can also clone the github repository and see the demos/filterjs.html

We look forward to some feedback from you and hopefully some contributions!

Filter away!

Update1

Thanks for the overwhelming response! I have added mustache templating for rendering the HTML view snippets.

Define mustache.js template in html page.

<script id="person_template" type="text/mustache">
<a href="/demo/{{id}}" title="{{name}}">
<span class="name">{{name}}</span>
<span class="age">{{age}}</span>
<div class="country">{{country}}</div>
</a>
</script>

View function:

var mustache_template = $("#person_template").html(); //Find template data.
var mustacheView = function(person){
return Mustache.to_html(mustache_template, person);
};

Posted in Javascript | Tagged , , | 23 Comments

Payment Gateway testing using webmock

Testing the payment gateway involves lots of scenarios like what should happen if exception is raised while doing payment. To test this scenario we have to call payment gateway API and take response. But herein lies a problem. The test code you have written may work sometime and won’t work another time. The reason behind this failure is, whenever  you call payment gateway in quick succession, then gateway gives error. So, we can’t test payment scenarios by calling actual gateway API. How can we then test gateway integration? The answer is  stubbing the gateway requests using webmock.

Testing recurring payment with authorize.net

  1. For configure ‘webmock’ for test environment and do bundle install.
  2. Executes your controller specs. Anyway, specs will fail as we disable all real HTTP requests.
  3. Download the recurring payment response from authorize.net using curl like below:

curl -H 'Content-Type:text/xml' <?xml version=\"1.0\" encoding=\"utf-8\"?>\n<ARBCreateSubscriptionRequest xmlns=\"AnetApi/xml/v1/schema/AnetApiSchema.xsd\">\n  <merchantAuthentication>\n    <name>2ErYn5tb5X</name>\n    <transactionKey>75mhe6B5TLNwA47b</transactionKey>\n  </merchantAuthentication>\n  <subscription>\n    <paymentSchedule>\n      <interval>\n        <length>1</length>\n        <unit>months</unit>\n      </interval>\n      <startDate>2011-09-14</startDate>\n      <totalOccurrences>1</totalOccurrences>\n    </paymentSchedule>\n    <amount>10.00</amount>\n    <payment>\n      <creditCard>\n        <cardNumber>370000000000002</cardNumber>\n        <expirationDate>2011-21</expirationDate>\n      </creditCard>\n    </payment>\n    <billTo>\n      <firstName>Test</firstName>\n      <lastName>Account</lastName>\n      <company></company>\n      <address></address>\n      <city></city>\n      <state></state>\n      <zip></zip>\n      <country></country>\n    </billTo>\n  </subscription>\n</ARBCreateSubscriptionRequest>\n" https://apitest.authorize.net/xml/v1/request.api > success_response

This sends an XMLHttpPost request to https://apitest.authorize.net/xml/v1/request.api and copies the response to success_reponse file.

We now have response in  hand. Place this repsonse in spec/support/authotize.net directory. Now, we need to configure the response such a way that whenever we send recurring payment request it should return our downloaded response.

Create a file called payment.rb in spec/support directory.

    #spec/support/payment.rb
    require 'webmock/rspec'

    RSpec.configure do |config|
     VALID_CARD  = 370000000000002
     FIRST_NAME   = "test"
     LAST_NAME    = "account"
     EXPIRY_YEAR  = (Time.now.year + 10).to_s
     EXPIRY_MONTH = (Time.now.month + 1).to_s
     CVV = "123"

     config.fixture_path = "#{::Rails.root}/spec/fixtures"
     # configure test response for purchase and recurring payment
     authorize_net = config.fixture_path + '/webmock/authorize.net/'
     payment_responses = []
     Dir["#{authorize_net}/**/*"].each do |path|
       payment_responses << path
     end
     payment_gateway = "https://apitest.authorize.net/xml/v1/request.api"
     success_params = []

     # configure recurring payment request parameters with different values
     10.times do |i|
       amount = 5 * i

       # Stubbing request parameters with different values
       success_params << "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<ARBCreateSubscriptionRequest xmlns=\"AnetApi/xml/v1/schema/AnetApiSchema.xsd\">\n  <merchantAuthentication>\n    <name>2ErYn5tb5X</name>\n    <transactionKey>75mhe6B5TLNwA47b</transactionKey>\    n  </merchantAuthentication>\n  <subscription>\n    <paymentSchedule>\n      <interval>\n        <length>1</length>\n        <unit>months</unit>\n      </interval>\n      <startDate>#{(Date.today + 1.month).to_s(:db)}</startDate>\n      <totalOccurrences>1</to    talOccurrences>\n    </paymentSchedule>\n    <amount>#{amount}.00</amount>\n    <payment>\n      <creditCard>\n        <cardNumber>#{VALID_CARD}</cardNumber>\n        <expirationDate>#{EXPIRY_YEAR}-#{EXPIRY_MONTH}</expirationDate>\n      </creditCard>\n    </payment>\n    <billTo>\n      <firstName>#{FIRST_NAME}</firstName>\n      <lastName>#{LAST_NAME}</lastName>\n      <company></company>\n      <address></address>\n      <city></city>\n      <state></state>\n      <zip></zip>\n      <country></country>\n    </billTo>\n  </subscription>\n</ARBCreateSubscriptionRequest>\n"

       config.around(:each) do |example|
         success_params.each { |parameter| WebMock::API.stub_request(:post, payment_gateway).
                with(:body => parameter,
                     :headers => {'Accept'=>'*/*', 'Content-Type'=>'text/xml'}).
                to_return(:status => 200,
                          :body => File.new(payment_responses[0]), :headers => {})}
       end
     end
    end

    WebMock.disable_net_connect!(:allow_localhost => true)
    example.call
    WebMock.allow_net_connect!

I think you understand how to stub recurring payment requests. Now we will see another example.

Purchase using authorize.net

As usual download purchase response from authorize.net like below:

   curl -H "Content-Type:application/x-www-form-urlencoded" -d login=2ErYn5tb5X -d key=75mhe6B5TLNwA47b -d key=x_type=AUTH_CAPTURE -d x_encap_char=%24 -d x_first_name=test -d x_invoice_num= -d x_last_name=account -d x_tran_key=75mhe6B5TLNwA47b -d x_card_num=370000000000002 -d x_version=3.1 -d x_relay_response=FALSE -d x_exp_date=1021 -d x_login=2ErYn5tb5X -d x_delim_data=TRUE -d x_description= -d x_card_code=123 -d x_amount=30.00 -d x_test_request=FALSE -d x_delim_char=%2C https://test.authorize.net/gateway/transact.dll > purchase
 

It will send as HTTP Post request to https://test.authorize.net/gateway/transact.dll and copy the response into purchase file. Place this file in spec/fixtures/authorize.net directory.

Now stub the purchase request:

   #spec/support/payment.rb
   stub_request(:post, "https://test.authorize.net/gateway/transact.dll")
        with(:body => /^[A-Za-z&_0-9#%.=]*$/,
             :headers => {'Accept'=>'*/*', 'Content-Type'=>'application/x-www-form-urlencoded'}).
        to_return(:status => 200, :body => File.new(payment_responses[1]), :headers => {})

Note: We can’t stub request with different parameters as it is a HTTP Post request. So match request parameters against regular expression.

Hope this helps! Please let me know if you see any complex payment scenarios. I will give a shot!.

Posted in Ruby on Rails, Tutorials | Tagged , , , | Leave a comment

Language independant translator using haml parser

Localization is usually necessary in every website. We wanted to add localization in an existing application with more than 100 pages! Now it would be crazyto go and change each view and add the unique keys in the .yml or .po file.

I google’d and I found some good reference here. Using this reference I made necessary changes  for parsing my .haml pages and enable for localization. Here is the solution for how to enable your view pages without changing view code and without adding I18n key for each plain text .

I am assuming you have I18n setup in your application! In my application I am using .po file for localization en.po (English) and hi.po (Hindi). For supporting .po file for localization you can use the i18n-translator-tools gem for details on how to use a .po file.

Use following steps for plain text translation

  • As per reference link  I added a rake task for collecting all plain text in my view pages and adding in .pot file

namespace :i18n_stuff do
  desc "Update pot/po files."
  task :create_po => :environment do
    require 'gettext/tools'
    require 'haml_parser'
    begin
      MY_APP_TEXT_DOMAIN = "mydomain"
      MY_APP_VERSION     = "1.1.0"
      GetText.update_pofiles(MY_APP_TEXT_DOMAIN,
          Dir.glob("{app/views}/**/*.{html.haml}"),
          MY_APP_VERSION,
          :po_root => 'config/locales')
    rescue Exception => e
       puts e
    end
  end
end

  • The haml_parser.rb file is used only in rake task is as below.

#This module used for crating the .pot file and
# this file conatins all views in pain text.

require 'rubygems'
require 'haml'
require 'gettext/tools'

class Haml::Engine
 # Override function that parses Haml tags
 # Inject gettext call for plain text action.
 def parse_tag(line)
 tag_name, attributes, attributes_hash, object_ref,
 nuke_outer_whitespace, nuke_inner_whitespace,
 action, value = super(line)

 @precompiled << "_(\"#{value}\")\n" unless action || value.empty?

 [tag_name, attributes, attributes_hash, object_ref,
 nuke_outer_whitespace, nuke_inner_whitespace, action, value]
 end

 def push_plain(text)
  @precompiled << "_(\"#{text}\")\n"
 end
end

The gettext.rb file is here:

# Haml gettext parser
module HamlParser
module_function

def target?(file)
  File.extname(file) == '.haml'
end

def parse(file, ary = [])
  bypass = ! File.basename(file, '.haml').match(
                                    /(vi|zh|zh_HK|id|th)$/).nil?
  puts "HamlParser:#{file}:bypass:#{bypass}"
  return ary if bypass

  haml = Haml::Engine.new(IO.readlines(file).join)
  result = nil
  begin
    code = haml.precompiled
    code = code.gsub("%","")
    code = code.gsub(/(.*#\{(_hamlout.adjust_tabs\(\d+\);\s*)?haml_temp)\s*=\s*(_\(['"].+['"]\))/) { |m| "haml_temp = #{$3}; #{$1}" }
    code = code.split(/$/)

    result = GetText::RubyParser.parse_lines(file, code, ary)
    # result = RubyGettextExtractor.parse_string(haml.precompiled, file, ary)
  rescue Exception => e
    puts "Error:#{file}"
    raise e
  end
  result
end

GetText::RGetText.add_parser(HamlParser)

  • Output of rake task. creating file config/locales/mostfit.pot
#: app/views/staff_members/new.html.haml:1
msgid "Create a new staff member"
msgstr ""

#: app/views/staff_members/_fields.html.haml:4 app/views/bookmarks/edit.html.haml:7
msgid "Name:"
msgstr ""

#: app/views/staff_members/_fields.html.haml:8
msgid "the screen name of the staff member"
msgstr ""

#: app/views/staff_members/_fields.html.haml:12
msgid "Mobile number:"
msgstr ""
  • Create en.po and hi.po using pot and add your translation text.
  • Now you are ready for your all plain text translation now you need to show you translation in browser. Using haml gettext module providing gettext translation for all Haml plain text calls. add following file in your config/init.rb

require "haml_gettext"

The haml_gettext.rb is here:

require 'i18n/gettext'

class Haml::Engine
  include I18n::Gettext::Helpers
  # Inject _ gettext into plain text and tag plain text calls
  # After getting all Haml plain text we are checking each word
  # conversion in .po file using method I18n::Gettext::Helpers
  # gettext()

  def push_plain(text)
    po_file_text = gettext(text, options = {})
    # this is method finding plain text in .po file and
    # return translator text
    text  = po_file_text if po_file_text
    super(text)
  end

  def parse_tag(line)
    tag_name, attributes, attributes_hash, object_ref,
    nuke_outer_whitespace, nuke_inner_whitespace,
    action, value = super(line)

    value = (value) unless action || value.empty?

    [tag_name, attributes, attributes_hash, object_ref,
    nuke_outer_whitespace, nuke_inner_whitespace, action, value]
  end
end

Now your application  ready for supporting localization without changing plain text in view. I shall post a github repos or a gist for a working edition for you shortly!

Posted in Ruby | Tagged , | 1 Comment

Stubbing Geo-location requests using webmock

Geo-location integration is common today for every web application. We have gems on hand, to integrate Geo-location to our application. With few steps of configuration we can Geo-code our attributes ie., user addresses. But testing Geo-coding integration takes much more time than development! We currently use Google Map APIs for our work. If there are a lot of  test cases, and if each test case made a Geo-location call, then we quickly cross the API threshold limit. It also takes more time to execute the test suite. It’s better to stub Geo-location calls so that we don’t need to bother with API limit and execution time.

To stub the call I am using Webmock gem which is used to stub HTTP requests. To know more about Webmock gem go through this link. For better understanding here I am going to explain this with a demo project. First clone the project from here and set up in your machine.

The main aim of the demo is Geo-code user address and displays latitude and longitude. To Geo-code the address I used ‘Geocoder’ gem. Here is the code to Geo-code an address:

   #app/models/user.rb
   geocoded_by :full_address
   after_validation :geocode

   def full_address
      [street_address, city, state, zipcode ].join(', ')
   end

It calculates latitude and longitude of the address given by the user. To calculate, it  calls the Google Map API and gets lat and long. So here we stub the call and return a predefined response that we already stored in our machine. To do that we have to do some webmock configuration. Here are the steps:

1) First download the response of the Geo-location using curl. We can find this in webmock documentation.

2) Place the response file under dir ‘spec/fixtures/webmock/maps.google.com/maps/api/geocode/’.

3) Configure the webmock as follows:

  #spec/support/webmock.rb
  require 'pathname'
  require 'webmock/rspec'

  RSpec.configure do |config|
    config.fixture_path = "#{::Rails.root}/spec/fixtures"
    dir = config.fixture_path + '/webmock'
    stubs = {}
    Dir["#{dir}/**/*"].each  do |path|
      next  if File.directory? path
      uri = path.dup
      uri.slice!( "#{dir}/" )
      if File.basename(uri) == '_directory'
         uri = File.dirname(uri)+'/'
      end
      stubs["http://#{uri}"] = path
    end

    config.around(:each) do |example|
      stubs.each { |uri, path|
         WebMock::API.stub_request(:get, uri).to_return(
                                             File.new(path))
      }
      WebMock.disable_net_connect!(:allow_localhost => true)
      example.call
      WebMock.allow_net_connect!
    end
  end

It’s restricts the HTTP requests which we are sending to external hosts except localhost. Whenever we made Geo-location call, instead of calling google API, webmock returns fixture file as a response. So we are getting lat and long without calling google API which is very interesting. Awesome, right? This also makes you test code execute very fast.

Note that WebMock disables all HTTP requests. To allow localhost, you need to specify :allow_localhost => true. Similary if you want to allow some other HTTP requests, say to Facebook, you can additionally pass :allow => [ 'www.facebook.com'] too!

Do go through the example code repository and execute the specs in demo for better understanding.

I hope you now understand the advantages of stubbing external API requests.Any feedback or suggestion would be welcome.

Posted in Ruby on Rails, Tutorials | Tagged , , , | 1 Comment

Testing social networks using capybara, cucumber-rails, selenium

Integration of social networks is very common in every application. We have many gems are available for doing integration. But testing this social integration is complicated task. Using capybara with cucumber we can easily test this similar to the normal UI testing through capybara.

Step 1:
Set up default driver to selenium.

#features/support/env.rb
Capybara.default_driver = :selenium

Step 2:
Generate cucumber feature and follow below steps to write step definitions:
1) To click on ‘Facebook’ login:

   page.find(:xpath, "//a/img[@alt='Fb-login-button']/..").click

Here I am using xpath selector to find facebook login button.’Fb-login-button’ alt value of my facebook button.

2) Fill up the Facebook login form. This will differ if we are doing login through a popup.

For popup login:

      begin
      within_window(page.driver.browser.window_handles.last) do
         fill_in('email', :with => Fb-Email)
         fill_in('pass', :with => Fb-password)
         click_on('Log In')
      end
      rescue Exception => e
         p "Selenium Exception: Session has no driver"
      end
   

Sometimes selenium will throw an exception. So its better to use exception handling.

For Standard Facebook login, just write:

       fill_in('email', :with => Fb-Email)
       fill_in('pass', :with => Fb-password)
       click_on('Log In')

Now your are done. You automated social networking testing. Follow above steps even for ‘twitter’ login also.

I hope everyone will enjoy this article. Any queries or suggestions would be welcome.

Posted in Ruby on Rails, Tutorials | Tagged , , , | Leave a comment

Ajax forms testing using capybara, cucumber-rails and selenium

Manual UI testing plays a crucial part in test life cycle. However doing the same UI testing on every other day will became tedious. By using cucumber with capybara we can transform these mundane tasks into some interesting learning. We can easily automate manual UI testing by using capybara with cucumber. While automating, ajax forms testing became a task that required some tweaking. I did some R&D to accomplish this task for my project and also that is what pushes me to share my R&D with you.

Task : Admin should be able to edit footer. By clicking on ‘Edit’ link it sends an ajax request and renders a form in facebox. After filling the form and clicks on ‘Add’ button it creates a footer and render a successful message.

Now we will see how to write feature to fulfill this task.

Step 1:

#features/manage_footers.rb
 @javascript
  Scenario: Footer update
    When clicked on edit link
    And updated footer name 'About Us'
    And updated category 'Learn more'
    And updated order as '1'
    And checked link to other page
    And clicked on add button
    Then user should be able to success message

Notice that above scenario there is tag ‘@javascript’ tag which we will do magic. It tells to capybara sends an ajax request for this scenario. Now we will see step definitions

Step 2:

#features/step_definitions/footer_steps.rb
 When /^clicked on edit link$/ do
    click_link('Edit')
 end
 When /^updated footer name 'About(\d+)'$/ do |arg1|
    within('#facebox') do
      fill_in('footer[name]', :with => 'About Us')
    end
 end
 When /^updated category 'Learn more'$/ do
    within('#facebox') do
      select('Learn More', :from => 'footer[category]')
    end
 end
 When /^updated order as '(\d+)'$/ do |arg1|
    within('#facebox') do
      fill_in('footer[sequence]', :with => 1)
    end
 end
 When /^checked link to other page$/ do
    within('#facebox') do
      check('footer[only_url]')
    end
 end
 When /^clicked on add button$/ do
   within('#facebox') do
     click_button('footer_submit')
   end
 end
 Then /^user should be able to success message$/ do
   page.has_content?('Footer was successfully updated.')
 end

Step 3:
Set default driver to selenium in env.rb file.

#features/support/env.rb
Capybara.default_driver = :selenium

Step 4:
Now execute the feature. Because we set default driver to selenium it opens firefox (by default) and executes the steps. For more read capybara.

I hope this article will reduce your manual testing effort. Any suggestions and feedback would be welcome.

Posted in Ruby on Rails, Tutorials | Tagged , , | Leave a comment

Factory_girl with Non-ActiveRecord classes

In earlier post we have seen how to test active record associations with factory girl. Now we will learn how factory girl works with non-active record classes. When we want to test external API responses, this will be useful.  By using this we can build no of responses at a time and test, how system will handle inputs. It is as similar as to working factory girl with active record classes. Now here is the way:

Step1:

First you need define class and attributes of the class. For example

#spec/support/models/deal.rb
class  Deal
   attr_accessor  :title, :remainingQuantity, :price_amount
end

Step 2:

Create a directory called ‘models’ inside ‘spec/support’ directory and place this class.  Because of following line of code it will includes our class while test environment is loading.

# spec/spec_helper.rb
Dir[Rails.root.join("spec/support/**/*.rb")].each {|f| require f}

Step 3:

Now we have to create an object by using factory. Create a factory file in spec/factories directory as how we are writing factory file for a active record class. For example

#spec/factories/deal.rb
Factory.define :deal do |g|
    g.title               "Whisky for $10"
    g.remainingQuantity   20
    g.price_amount        10
end

Step 4:

Now use this factory in your specs. For example

#spec/models/deal_spec.rb
deal = Factory.build(:deal)

It will return an object of class Deal. Now it works as a normal ruby object and we can apply our conditions on it. For more see factory_girl

I hope everyone enjoyed this article. Any comments would be welcome.

Posted in Ruby on Rails, Tutorials | Tagged , | 2 Comments