Rss Feed Tweeter button Facebook button Technorati button Reddit button Linkedin button Webonews button Delicious button Digg button Flickr button Stumbleupon button Newsvine button

A Waage Blog

Ruby, Rails, Life

Archive for the ‘Rails Testing’ Category

Ruby floats, BigDecimals and money (currency)

without comments

Fellow Ruby-ers, please be warned!!! DO NOT use Ruby floats when performing arithmetic calculations involving money!

My calculations work in IRB, so I was really confused when I ran into this weird situation where (what I thought was) a simple arithmetic calculation led to strange results in my unit tests (I cannot stress the importance of good unit testing!).

My backend calculation was basically this (simplified):

# arbitrary amounts for these two variables
percentage = 12
total_in_cents = 400

discount = percentage.to_f / 100.0
total_in_float = total_in_cents.to_f * 100.0
new_price = (total_in_float * discount ).round / 100

Now, it’s pretty obvious that 12% of (400 cents) $4.00 should just be $0.48 (48 cents)
However, my barrage of unit tests kept producing strange results where a simple calculation was returning incorrect results. Doing some research, I discovered a series of articles worth reading including:

Also, check out the Money gem – I’ve never used it personally, but people have said good things about it.

Heeding the advice I found online, I re-wrote all my money-related calculations using BigDecimals instead of Floats.

percentage = 12
total_in_cents = 400
discount = BigDecimal(percentage.to_s) / 100
total_in_float = BigDecimal(total_in_cents.to_s) * 100
new_price = (total_in_float * discount ).to_i / 100

After switching over from Floats to BigDecimals, my unit tests all passed!
Lesson learned and hope this heads-up helps you guys too.

Summary:
Use BigDecimals for money calculations and remember to write good UNIT TESTS!!

Written by Andrew Waage

November 9th, 2011 at 5:29 pm

Rails 3 RSpec Request Spec – Testing Subdomains

without comments

How do you test sub-domains in RSpec Request specs (integration tests) ???

# Pass it into the GET request!
get '/programs/100', nil, {'HTTP_HOST' => 'sub.domain.com'}

The 3rd parameter to the get method is a hash of HTTP headers.
See the Rails API documentation for details.

Note:
Depending on the type of test you are working with (support / controller / request / integration etc.) you pass in the sub domain differently.

Here’s some good reference posts on Stack Overflow for setting subdomains in controller specs:
1. Rails RSpec Set Subdomain

# Set the @request.host in a before block
before(:each) do
  @request.host = "#{mock_subdomain}.example.com"
end

2. Subdomains in RSpec Controller Tests

 # I haven't tried this, and not sure you would need to mock out the current_subdomain method.
  @subdomain = 'sub.domain.com'
  controller.expects(:current_subdomain).returns(@subdomain)
  @request.host = "#{@subdomain}.test.host"

Written by Andrew Waage

July 11th, 2011 at 3:43 pm

RSpec – Running One Single Test at a Time

without comments

In the old days I would pass a regular expression to run a particular unit test or group of similarly named unit tests by name.

Here’s the easy way to run one test in RSpec… by line number!
Look at the line-number of any RSpec block (it, describe, etc), and simply run the rspec command, passing in the [filename]:[line number]:

$ rspec models/user_spec.rb:27

Happy testing!

Written by Andrew Waage

July 11th, 2011 at 3:03 pm

RSpec Request Spec to Test Rails / Grape API Functionality

with one comment

I finally got around to trying Grape – a “RESTful API microframework built to easily and quickly produce APIs for Ruby-based web applications”. This is a project still in baby stages, but has a lot of potential and worth exploring for anyone creating a Rack-based API in Ruby, not necessarily Rails!

Now, after creating a pretty basic API that used HTTP Basic Authentication, I was inclined to write some RSpec tests to make sure my API was working the way I thought it was (.. or because I am obsessed with well-tested, beautiful code..).

After some thought, I decided that the best way to test my API was with RSpec “request” specs. Now, if you are at all relatively new to RSpec (I was a Test::Unit kinda guy before), it might not be completely obvious that “request specs” are basically what I have come to know as “integration tests”, testing high-level functionality that spans multiple controllers and multiple requests – (think: a user’s interaction with the app).

My reasoning for choosing request specs is because I want to test specific API URL endpoints routed the way I expected. (Routing is handled magically by Grape with a simple mount in the config/routes.rb file). API testing just kinda makes sense to handle in request specs.

Anyways, I ran into a couple issues because in REQUEST specs, you do not have access to the @request object (haha?), as you do in controller specs. Now, in order to mock HTTP Basic Authentication, you need to mock the request object to send headers along with the GET request.

Well, solution: It turns out you can pass headers into your get() method! I only wish I had discovered that an hour ago!

Here’s a simple excerpt from my API request specs that shows how to mock the HTTP basic authentication and test your API functionality:

With NO basic auth, it’s just a simple GET request

  it 'should return a 401 with no basic auth to /api/v1/rewards' do
    get '/api/v1/rewards'
    response.code.should == '401'
    response.body.should == "Unauthorized - Please check your username and password"
  end

To mock the basic auth, simply pass header hash as argument to the GET request! No need to access the request object here.

  it 'should return a 200 with valid basic auth to /api/v1/rewards' do
    # Uses basic_auth helper method
    credentials = basic_auth('testuser','test')
    get '/api/v1/rewards', nil, {'HTTP_AUTHORIZATION' =>  credentials }
    response.code.should == '200'
    response.body.should == "..."
  end

# You can define this at the bottom of your spec file, or in spec_helper for convenience
def basic_auth(user, password)
  ActionController::HttpAuthentication::Basic.encode_credentials user, password
end

Hope this helps someone else. Now go write some request specs! :)

Written by Andrew Waage

May 26th, 2011 at 1:29 am

Functional Testing Cookies in Ruby on Rails 1.2.3

with 2 comments

After spending a good couple hours + trying to figure out why my functional tests were failing when working with cookies, I came across a couple great resources for rails + testing cookies.

Here’s a good basic explanation of using cookies on Pluit solutions website. Then I came across the Robby on Rails blog that led me to understand (and misunderstand) the asssert_cookie plugin.

However, I was still having a lot of trouble figuring out why I was setting my @request.cookies['foo'] in my functional test, but later (in the same test method) checking for cookies['foo'] and not finding anything in the cookie jar!

I then tried setting cookies['foo'] in my controller test, but found that when I did a check for the existance of that cookie in my actual controller code, it was not there! I thought that I should be able to set up a cookie in a test and have my controller recognize it. This is true, however, not by setting cookies['foo'].

So… I opened up a blank controller and functional test and began testing out different combinations to find out exactly what was going on. I hope this little example helps to save someone else’s precious time. Here it goes…

This is the controller I will be referring to for my example tests:

========= controller
  def foo
    if cookies['oreo'] == 'yesitis'
      @info =  "Oreos are tasty"
    else
      @info = "Only Milk here"
    end
     render :nothing => true
  end

1. In your functional test, do not test for presence of a cookie after a get request to a particular action, unless the action you are testing specifically sets up that cookie. In other words, if your controller action is not setting the cookie, do not test for that cookie’s presence.

In the above controller, no cookies are ever being set. Therefore, testing for the presence of a cookie after the get request (in a test method) will return false.

In other words, the cookie is recognized by the controller, however, notice that cookies['oreo'] is nil after the get request:

  def test_oreo
    @request.cookies['oreo'] = CGI::Cookie.new("oreo", "yesitis")
    get :foo
    assert_equal({}, @response.cookies)
    assert_equal(nil, cookies['oreo'])
    #Assert cookie will be nil here so do not test it:
    #assert_cookie :o reo
    puts assigns['info'] #"Oreos are tasty"
  end

Note:
1. After your get request, the @response.cookies will be empty
2. After your get request, cookies['oreo'] will still be nil.
3. After your get request, you cannot use assert_cookie because it will be nil

2. If your controller action expects a cookie to be there, you cannot set it up in your functional test by assigning:

  • cookies['oreo'] = true (incorrect) OR
  • @request.cookies['oreo'] = true (incorrect)
  • The only way to set up your cookie for a subsequent request, so that your controller will see it is:

    • @request.cookies['oreo'] = CGI::Cookies.new(’oreo’, true) (correct)
      #INCORRECT
      def test_oreo
        cookies['oreo'] = 'yesitis'
        get :foo
        puts assigns['info'] #"Only Milk here"
      end
    
    #INCORRECT
      def test_oreo
        cookies['oreo'] =  CGI::Cookie.new("oreo", "yesitis")
        get :foo
        puts assigns['info'] #"Only Milk here"
      end
    
    #INCORRECT
      def test_oreo
        @request.cookies['oreo'] = 'yesitis'
        get :foo
        puts assigns['info'] #"Only Milk here"
      end

    The only way to set up your cookie in a functional test and have your controller recognize it.

    #CORRECT
      def test_oreo
        @request.cookies['oreo'] = CGI::Cookie.new("oreo", "yesitis")
        get :foo
        puts assigns['info'] # Will print out "Oreos are tasty"
      end

    3. Avoid the temptation to use symbols in your tests with cookies.

    • cookies[:foo] -> BAD
    • cookies['foo'] -> GOOD

    4. Only use assert_cookie (plugin: see above for link) if you are testing that the controller action is creating a new cookie.

    5. If you are testing multiple actions and redirects from one action to another action in the same controller, you do not need to set the cookie before each action, explicitly. The cookie will remain in the @request object that you created in the setup method of your test.

    EXAMPLE TWO (Testing persistent cookies)
    You DONT set up the cookie for each subsequent request.
    It does “persist” throughout your test-method because you are
    using the same @request object!!

    ========controller
      def foo
        if cookies['oreo'] == 'yesitis'
    	puts "Foo has OREO"
        else
     	puts "Foo has NOTHING"
        end
          redirect_to :action => :bar
          return true
      end
    
      def bar
        if cookies['oreo'] == 'yesitis'
          puts "Oreos are tasty"
        else
          puts "Only Milk here"
        end
         render :nothing => true
      end
    
    ========= Test
      def test_oreo
       @request.cookies['oreo'] =  CGI::Cookie.new('oreo', "yesitis")
        get :foo    #OUTPUTS: Foo has OREO
    
        # COOKIE PERSISTS HERE!!
        # Don't need to explicitly set cookie again in the
        # @request.cookies object
        get :bar   #OUTPUTS: Oreos are tasty
      end

    Written by Andrew Waage

    October 13th, 2008 at 7:33 am