Pondering Pickled Patterns
cucumberFor me Pickle is what made Cucumber make sense to me for integration testing. I had a hard time coping with the idea of spending so much time going between nearly English features and huge swaths of regular expressions. Pickle cuts down on a lot of the regular expression drudgery by defining some generic steps for working with models. If you haven’t tried Pickle out yet, I highly recommend it. Railscast 186 gives a great overview.
In addition to the generic step definitions Pickle provides, it also
gives you access to the powerful regular expressions that drive those
steps. I just spent some time hung up on one of those regexps,
capture_model
.
In the feature I was creating a labeled account and then visiting the users page for that account:
Given an account: "spectre" exists
When I go to the users page for the account: "spectre"
Then I was using capture_model
in a pattern in
features/support/paths.rb
like so:
case page_name
# ...
when /the users page for #{capture_model}/
account = model($1)
users_path(:subdomain => account.subdomain)
# ...
end
Unfortunately, every time I ran the feature, #model
would
return nil
. After banging my head into this for a while I
finally determined that #capture_model
was capturing an
empty pattern, so I was passing ""
to #model
.
I printed out the the regexp returned by #capture_model
to see what was going on (brace yourself):
/((?:(?:)|(?:(?:a|an|another|the|that) )?(?:(?:(?:(?:first|last|(?:\d+(?:st|nd|rd|th))) )?(?:account|user))|(?:(?:account|user)(?::? \"(?:[^\\"]|\.)*\")))))/
The regular expression is built up programmatically at runtime based on
the models which currently exist in your application. (As you can see
above, in the application I was working on I only had Account and User
models defined). The regexp is long and looks a little like Lisp code
that’s been tweaking, but if you look closely you’ll probably come to
realize (faster than I did) that it will happily match an empty string.
In fact, in the pattern I was using in paths.rb
#capture_model
was matching an empty string and the rest
of the line was being thrown away. Unfortunately that part – the
account: "spectre"
– was the most important part…
Realizing where I was going wrong I anchored my pattern to the end of the line:
when /the users page for #{capture_model}$/
Woo! Now the line only matches when everything between “the users page
for “ and the end of the line can be matched by
#capture_model
. With this addition, the regexp captured
the model label account: "spectre"
and #model
finally returned the record. Allons-y!
The moral of the story: anchor your patterns whenever possible. It’s generally good practice if only to make sure you acting on the correct data.