Tag Archive | cucumber

Step through your cucumber features interactively

NOTE: This is probably only useful for features that run in browser mode i.e. NOT headless. Using Rails 3 and Capybara, these would be features tagged as @javascript.

Do you find yourself using the save_and_open_page method to debug your cucumber features? Or how about raising exceptions to break the scenario execution? I guess it works, but not very elegant…

One easy way to pause the scenario execution after each step is to use the cucumber callbacks.

Start by creating a hooks.rb file in your features/support folder. You can name the file whatever you want, but it must have a .rb extension. Then add the following snippet to your new file:

AfterStep('@pause') do
  print "Press Return to continue..."
  STDIN.getc
end

What we’re doing here is using the AfterStep callback to execute our code after each cucumber steps has finished. The parameter is telling cucumber to only do this for features that have the @pause tag. We then wait for a carriage return from the console before moving to the next step.

Now all you have to do is tag a feature with @pause and you will have to press Return after each step to walk through the scenario. This makes debugging a snap when you’re using something like Firebug etc.

Try it out for yourself.

Run your specs and features in different environments

By default your specs and cucumber feature both run in the test rails environemnt i.e. RAILS_ENV=test.

However, I need these two environments to be different. First I’ll show you how, then I’ll tell you why.

I want to have my feature run in a cucumber environment i.e. RAILS_ENV=cucumber. Simply edit the features/support/env.rb file and update the default environment value so that you file looks like this:

ENV['RAILS_ENV'] = 'cucumber'
Rails.env = 'cucumber'

We still want our feature to hit the test database, so simply create a pointer in your database.yml file to do this:

test: &TEST
  adapter: ...
  database: ....
  host: ...

cucumber:
  <<: *TEST

Lastly, make sure you have a config/environments/cucumber.rb file for your new cucumber environment. I normally just copy the test environment file.

Now when you run your features they will run in their own, separate environment. Groovy.

Optional: You may also want to modify your Gemfile by adding an additional :cucumber group. I’ve never done this and never run into any problems, but bear it in mind.

Why do I need this?

When I run my specs I use spork and autotest. This makes running specs lightning fast. However, you do need to make some tweaks to the test.rb environment file, namely turning class caching off. This is to make sure your specs always run with the latest saved versions of your models.

config.cache_classes = false

For cucumber, you can leave caching on. However, I use database_cleaner for my features with a truncation strategy. This makes the running of selenium features reliable because no transaction has to be rolled back after each scenario – the data is simply truncated.

Simples.

Use FactoryGirl steps in Cucumber features

I came across something VERY useful recently: factory_girl‘s built in cucumber steps. That’s right, factory_girl comes with some built in cucumber steps that can be used in your cucumber features.

Just add the following to your cucumber env.rb file:

require "factory_girl/step_definitions"

Then, assuming you have the actual factory definitions in the first place, you can use all kinds of loveliness, including the use of associations!

For more detailed information, check out Brandon Keepers’ Practical Cucumber series.

Speed up your features with a backdoor login route

In any application that tracks users, I’m guessing that the vast majority of features will require that a user is logged in. This however can add a huge amount of overhead to your features at run time.

To speed things up, you can create a backdoor route to allow your features to bypass the login page and form submission, and simply hit a url to create the session.

First, define the route scoped by a condition – you don’t want this in production mode!

if %w( development test cucumber ).include?(Rails.env.to_s)
  map.backdoor '/backdoor', :controller => :user_sessions, :action => :backdoor
end

Then define the controller action. Bear in mind we are using authlogic. Adjust this to suite your own requirements.

# for cucumber testing only
def backdoor
  @user_session = UserSession.new(User.find_by_email(params[:email]))
  @user_session.save
  head 'ok'
end

Finally, your cucumber step goes from something like this…

Given /^I authenticate successfully$/ do
  Given 'I am on the login page'
  And 'I fill in "Email" with "eddie@hotmail.com"'
  And 'I fill in "Password" with "secret"'
  When 'I press "Login"'
  Then 'I should see "Welcome, Service Account (AD)"'
end

…to something like this…

Given /^I am already logged in as a user$/ do
  visit "/backdoor?email=eddie@hotmail.com"
end

We shaved nearly 2 minutes off our 12-minute build!

Thanks to this post for the original idea.

Find your slowest-running cucumber features

Our feature set has 275 scenarios with 1175 steps, and it’s growing every day. In total it takes about 11 minutes to run the full set of features. We wanted identify the 20 slowest scenarios so that we could do some refactoring both in our code and tests to make things run a bit faster.

Add the following code to your features/support/env.rb file. All we do is catch each scenario using the Around function and grab the execution time for each scenario. Then display he results at the end of your build process.

scenario_times = {}

Around() do |scenario, block|
  start = Time.now
  block.call
  scenario_times["#{scenario.feature.file}::#{scenario.name}"] = Time.now - start
end

at_exit do
  max_scenarios = scenario_times.size > 20 ? 20 : scenario_times.size
  puts "------------- Top #{max_scenarios} slowest scenarios -------------"
  sorted_times = scenario_times.sort { |a, b| b[1] <=> a[1] }
  sorted_times[0..max_scenarios - 1].each do |key, value|
    puts "#{value.round(2)}  #{key}"
  end
end

This will give you output that looks like this:

------------- Top 20 slowest scenarios -------------
9.49   features/client_mapping.feature::Map clients with invalid upload file
8.42   features/client_mapping.feature::Map clients with valid upload file
7.35   features/indicators.feature::Show indicators after client upload
4.50   features/manage_clients.feature::Delete client
..
..
..

Hope this helps someone.

Stub your external endpoints using WebMock

On one of my projects we integrate with 2 external data suppliers through their APIs. In today’s development world this is fairly common and quite easy to do in terms of the integration.

However, the tricky part is figuring out how to test this properly. Using RSpec it’s easy to test models by stubbing out the functions which call the external services. However, when we run our cucumber features we can’t simply stub a method call on a model.

Hello WebMock

WebMock allows you to stub out your external endpoints at the HTTP level. In other words, you stub the external service and response and then allow your application to perform the remote request normally. Within the scope of your application this is a real cucumber feature i.e. an integration test.

Here’s how we use it…

Add the following code to your features/support/env.rb file:

require 'webmock/rspec'
World(WebMock::API, WebMock::Matchers)
WebMock.allow_net_connect!

The final line tells WebMock to allow all requests to proceed to the network as expected. This is important because we are running cucumber features.

Next, in the relevant cucumber step we will specify the behaviour for WebMock.

Given /^the remote service will return a valid response$/ do
  stub_request(:any, 'http://localhost:4567').to_return do |request|
    body = CGI::unescape(request.body)
    code = body.match(/<CODE_NBR>(.*)<\/CODE_NBR>/)
    response = IO.read("#{RAILS_ROOT}/test/responses/#{code[1]}.xml")
    {:body => response, :status => 200}
  end
end

By passing a block to the stub_request method we can make WebMock behave in a more dynamic way, without having to stub out each individual request. In this case, we search the request body for a known tag which contains a unique code. We use that code to return an expected XML response we have saved. That’s it! Our cucumber features call our external services and process the responses as expected.

What did we do before?

We previously had a Sinatra app which would return the same pre-rolled responses used above. The behaviour was essentially the same, but it was complicated and it made our cucumber steps harder to follow and debug. It also meant for each test run we had to kick off and tear down WEBrick processes to run Sinatra properly – ugh! It was very clever but the kind of thing you have to re-learn every time you look at it.

And now…

Our features are now much cleaner, a little faster, but most importantly, readable and maintainable.

Follow

Get every new post delivered to your Inbox.