Michael MacDonald

Archive for the ‘Rails’ Category

Testing Scopes with Lambdas

In Rails, Testing on February 24, 2012 at 3:50 pm

How do you know that your scopes are correct? At work we had been plagued by mysterious multiple payments in one of our apps. It had been coded to prevent multiple payments yet some customers were somehow managing to do it. After a thorough investigation, I discovered that the problem simply came down to one of the scopes used in the code had been written incorrectly. Here it is:

scope :current, where('start_date <= ?', Date.today).where('end_date > ?', Date.today)

Do you see the problem? It is a scope that checks the object is current, that it has started but not yet ended. Unfortunately, this scope is missing a lambda.

scope :current, lambda { where('start_date <= ?', Date.today).where('end_date > ?', Date.today) }

Without the lambda, the scope is set to the date when the scope was first loaded so that Date.today remain static until the next deploy or when the production app is restarted. So it’ll work fine for a day and then it’ll start misbehaving. The lambda effectively defer the evaluation of the expression until it is needed. So each time you use Authorisation.current it’ll use the current date which is what you want.

So how do we avoid this mistake? Here’s the wrong way to test a scope that uses a lambda:

expired_auth = Authorisation.create(start_date: 4.days.ago, end_date: 2.days.ago)
current_auth = Authorisation.create(start_date: 2.days.ago, end_date: 2.days.from_now)

Authorisation.current.should include(current_auth)
Authorisation.current.should_not include(expired_auth)

This will pass the original non-lambda scope. It passes because it’s not really testing the dynamic nature of the scope provided by the lambda. For this I use the Timecop gem:

Timecop.freeze(Time.now)
auth = Authorisation.create(start_date: 2.days.ago, end_date: 2.days.from_now) # or use a factory to give you a current auth
Authorisation.current.should include(auth)

Timecop.freeze(2.days) # fast forward by 2 days so that the current auth is now by definition no longer current
Authorisation.current.should_not include(auth)

Now this test fails with the non-lambda scope. When I change the scope to use lambda, the test now passes.

Advertisements

Faster Cucumbering With Pagination

In Rails, Testing on November 25, 2010 at 5:30 pm

When writing a cucumber feature that involves pagination, the easiest thing to do is to create the required number of objects to generate pagination.

Given 31 tasks exist  # pagination defaults to 30

The downside of course is speed. Creating a large number of complex objects can add a lot of extra wait time to your cucumber runs. In my case, although I only had a few scenarios that involved pagination, my per_page setting was 50 and the objects I was creating were complex. The end result was a extra couple of (unnecessary) minutes. It wasn’t too bad so I left myself a TODO to fix it one day and that day finally came. Read the rest of this entry »

Javascript Testing with Cucumber, Capybara and env.js

In Rails, Testing on June 12, 2010 at 9:05 am

Tim Riley was excited when he discoved capybara and wrote an excellent blog post about Javascript Testing with Cucumber and Capybara. I was excited too but it took until I was sitting in a hallway at railsconf recharging my laptop before I found the time to clone Tim’s demo app and give capybara a try. I was impressed! I ran the tests and Firefox opened up in the background, ran the test and closed almost in a blink of the eye. Wow, that was fast. So much faster than how I’ve previously tested my javascript with selenium and webrat. And no complicated setup or configuration. Read the rest of this entry »

Pickle Tables

In Rails, Testing on November 26, 2009 at 12:12 am

Following on from my original and followup articles on using Pickle with Cucumber, a brand new version of Pickle is now available. Version 0.2.0 now gives you the ability to use Pickle with Cucumber’s multiline step argument tables. This means you can now do the following with Pickle:

Given a company exists
And another company: "rubyflare" exists
And the following people exist:
  | name  | age | employed | company              |
  | Digby | 13  | false    | the first company    |
  | Ethyl | 27  | true     | company: "rubyflare" |
Then the following people should exist:
  | name  | age |
  | Digby | 13  |
  | Ethyl | 27  |
And the 2nd person should be one of company: "rubyflare"'s employees

You can quickly create multiple objects in an easy to read fashion rather than filling your scenarios with multiple lines of “an another user exists …”. As an added bonus you can also use Pickle references within these tables. In the example above, we are creating new people objects and associating them to the companies using the Pickle references for these company objects.

Again, I’ll just point out that the above example required no custom step definitions. I didn’t have to write any step definitions! Pickle takes care of the grunt work leaving you free to focus on your domain specific scenario steps.

Other recent improvements with Pickle include:

  • an email helper for mapping names to email addresses similar to paths
  • its own separate configuration file (pickle.rb) – pickle no longer adds code to features/support/env.rb

I’ve also updated my pickle_example project to the latest version and have added the above example scenario. Feel free to clone it and use it to explore the benefits of using Pickle.

More Pickle Action

In Rails, Testing on November 3, 2009 at 8:00 am

In a previous blogpost I championed the benefits of using Pickle with Cucumber to speed up your BDD. Recently, I was able to contribute back to Pickle in a small way with the following two changes:

This means you can do things like this in your plain text cucumber steps:

Given a user exists with name: "Digby", bank_balance: -43.25

And if you need to, you can also be explicit with the positive sign:

And another user exists with name: "Miranda", bank_balance: +86.50

The extra step definitions allow you to do the following:

Then a user should not exist with name: "Maya"
And the first user should not be one of the last user's debtors
And the last user should not be the first user's creditor

These are the “not” versions of some of the existing default step definitions. I found that I was needing these in my Rails projects and thought they would be good to have in the standard Pickle. Ian White, the creator of Pickle, agreed.

It feels good to contribute back to an open source project, even in a small way, and I encourage you all to try to do so as well.

Pickle my Cucumber!

In Rails, Testing on October 28, 2009 at 8:00 am

If you are using Cucumber then I would highly recommend using Ian White‘s fantastic little add-on called Pickle.

Pickle makes life in the Cucumber world so much easier by providing you with convenient step definitions that take care of common object operations leaving you free to focus on the fun stuff. Combined with the pre-packaged Webrat steps you can dramatically reduce the number of step definitions you need to write.

Read the rest of this entry »

Testing Sphinx with Cucumber

In Rails, Testing on September 2, 2009 at 8:00 am

For awhile, all of my Cucumber features that involved search were marked as TODO. The search worked but I had no integration tests for it because the default setup of Cucumber uses the transactional fixtures setting to run each scenario inside of a transaction to ensure that the database starts in the same known state for each scenario. Unfortunately ThinkSphinx won’t work with this setup. Thankfully, Brandon from opensoul.org wrote up a great post on how to modify your Cucumber setup to get it working.

Further to Brandon’s post, here’s how I’ve incorporated running search features with Cucumber… Read the rest of this entry »

Less Brittle View Specs

In Rails, Testing on August 26, 2009 at 8:00 am

View specs are often misunderstood and ignored (poor little things) however I believe they can be useful. A big criticism of view specs is that they are brittle – when your html changes, they break. Another is that Cucumber makes them redundant. A lot depends on how you write them. Read the rest of this entry »

Pseudo Natural Sort with Sphinx

In Rails on July 29, 2009 at 8:00 am

The Background:

  • items have a system code
  • all system codes begin with the same string of text eg S-
  • existing system codes have zeros to pad out the string so that all codes are of equal length eg S-001, S-011
  • users need to be able to search for system codes
  • using Sphinx and the awesome ThinkingSphinx gem

The Problem:

  • the business wanted to ditch the unnecessary zeros in the system codes to match the format of other codes in the system eg X-1, C-20, S-14
  • a search results table needed to be sortable by the system codes column eg S-1, S-2, S-10 instead of S-1, S-10, S-2 (the latter is what you get since it is an alpha sort on the whole string)
  • I flippantly proclaimed that we were smart enough to sort the system codes correctly and could easily ditch the redundant zeros

The Options:

  1. eat my words and keep the system codes as they are eg S-01, S-02, S-10
    • it would work but I don’t like letting the computer win
  2. as above but use a view helper to hide the zeros used for padding
    • not acceptable since it would display system codes as S-1 and users would need to search for S-01
  3. remove the prefix from the system codes, change them to integers and use a view helper to add back the prefix
    • not acceptable since the users need to search on the system codes eg S-1 instead of just 1
  4. or somehow get Sphinx and ThinkingSphinx to sort them properly
    • Sphinx can’t handle a natural sort but maybe there is a hack

The Hack:

has "CAST(MID(systems.code, 3, LENGTH(systems.code) - 2) AS UNSIGNED)", :type => :integer, :as => :system_code_sort

Yes, it is only a “pseudo” natural sort as it doesn’t sort the alpha characters but in my specific case it did the job and solved the specific need.

Use response.capture instead

In Rails, Testing on July 15, 2009 at 8:00 am

Recently, I upgraded an app from Rails 2.1.0 to Rails 2.3.2. First, I upgraded rspec and rspec-rails to 1.2.7, raked my specs and they all passed. But after I upgraded to Rails 2.3.2, one particular spec failed with the following error message:

"undefined method downcase' for :extra_info:Symbol"

Read the rest of this entry »