Understanding database cleaning strategies in tests

TLDR: In tests you need to clean out the database before each example. Use :transaction where possible. Use :deletion for Selenium features or when you have a lot of MyISAM tables.

Understanding database cleaning

You want to clean out your test database after each test, so the next test can start from a blank database. To do so you have three options:

  • Wrap each test in a transaction which is rolled back when you're done (through DatabaseCleaner.strategy = :transaction or `config.use_transactional_fi...

How to provoke Selenium focus issues in parallel test processes

As attachments to this card you will find a Cucumber feature and supplementing step definition that you can use to provoke Selenium focus issues that only occur when two focus-sensitive Selenium scenarios run at the same time (probably with parallel_tests). This can help you to detect and fix flickering integration tests.

The attached feature works by going to your root_path and focusing a random form element every 5...

How to test if an element has scrollbars with JavaScript (Cucumber step inside)

The basic idea is pretty simple: an element's height is accessible via the offsetHeight property, its drawn height via scrollHeight -- if they are not the same, the browser shows scrollbars.

var hasScrollbars = element.scrollHeight != element.offsetHeight;

So, in order to say something like...

Then the element "#dialog_content" should not have scrollbars

... you can use this step (only for Selenium scenarios):

Then /^the element "([^\"]+)" should( not)? have scrollbars$/ do |selector, no_scrollbars|
  scroll_heig...

How to find out the currently focused DOM element with JavaScript

This works in all relevant browsers:

document.activeElement

You can use this in your Selenium steps, for example, to assert that a form field is or is not focused.

Custom error pages in Rails

Basic error pages

To add a few basic styles to the default error pages in Rails, just edit the default templates in public, e.g. public/404.html.

A limitation to these default templates is that they're just static files. You cannot use Haml, Rails helpers or your application layout here. If you need Rails to render your error pages, you need the approach below.

Advanced error pages

  1. Register your own app as the applicatio...

How to change the hostname in Cucumber features

Capybara uses www.example.com as the default hostname when making requests.
If your application does something specific on certain hostnames and you want to test this in a feature, you need to tell Capybara to assume a different host.

Given /^our host is "([^\"]+)"$/ do |host|
  page.config.stub app_host: "http://#{host}"
  
  # In older Capybaras (< 2.15) you needed to do this instead:
  Capybara.stub app_host: "http://#{host}"
end

You can now say:

When I go to the start page
Then I should not see "Home ...

Don't name columns like counter_cache columns in Rails pre v4.2.4

< Rails v4.2.4

ActiveRecord has a feature called counter caching where the containing record in a has_many relationship caches the number of its children. E.g. when you have House has_many :rooms, Rails can cache the number of rooms in House#rooms_count.

Mind that when a model has a column that looks to Rails like a counter-cache column, Rails will apply counter-cache logic to your model, even if you're not using counter caches.

E.g. you have a house with 12...

New makandra cards feature: Repeating cards

Cards can now be flagged as "Repeating".

Every Wednesday, some of these cards will be moved back to the top of the deck.

This is intended for important cards that you want your team to occasionally repeat, so they aren't lost in the depths of your deck.

For your own deck, you can set the number of cards to repeat in your Deck Admin.

How to solve Selenium focus issues

Selenium cannot reliably control a browser when its window is not in focus, or when you accidentally interact with the browser frame. This will result in flickering tests, which are "randomly" red and green. In fact, this behavior is not random at all and completely depends on whether or not the browser window had focus at the time.

This card will give you a better understanding of Selenium focus issues, and what you can do to get your test suite stable again.

Preventing accidental interaction with the Selenium window
--------------------...

imgAreaSelect - image selection/cropping jQuery plugin

imgAreaSelect is a jQuery plugin for selecting a rectangular area of an image. It allows web developers to easily implement image cropping functionality, as well as other user interface features, such as photo notes (like those on Flickr).

Capybara 2.0 has been released

The gem author Jonas Nicklas highlights in a Google Groups post that the release

  • is not backwards compatible to 1.x versions of Capybara
  • does not support Ruby 1.8.x anymore
  • removes confusion with Rails' built in integration tests (you put capybara rspec integration tests into the spec/feature/... folder) and the :type metadata has been changed from :request to :feature
  • throws exceptions when trying to interact with an element whose identifier is...

Capybara: evaluate_script might freeze your browser

Capybara gives you two different methods for executing Javascript:

page.evaluate_script("$('input').focus()")
page.execute_script("$('input').focus()")

While you can use both, the first line (with evaluate_script) might freeze your browser window for 10 seconds.

The reason is that evaluate_script will always return a result. The return value will be converted back to Ruby objects, which in case of complex objects (e.g. a jQuery collection) is very expensive.

Because of this we recommend to only use evaluate_script whe...

Keyboard Navigation Plugin for Safari, Chrome and Firefox with a rich feature set

gleeBox is an experimental project that takes a keyboard-centric approach to navigating the web. It provides alternatives to actions that are traditionally performed via the mouse. Some of these are radically more efficient than using a mouse, some not so much. In all cases, they are mostly meant for keyboard and command line lovers.

Gleebox is a terrific plugin that makes navigating through webpages a wink. It even allows for selecting by jQuery selector.

Cucumber: Wait for any requests to finish before moving on to the next scenario

Background

Generally, Selenium tests use the browser to interact with the page. If it's unavailable, a timeout error is thrown.

Now, consider a scenario like this:

@javascript
Scenario: Receive an e-mail after clicking the fancy link
  When I follow "fancy link"
  Then I should have an e-mail with the subject "Hello"

When the last step in the scenario passes, you are done. Right? Wrong.

Why it's not enough

What if clicking our "fancy link" above sends the e-mail that we expect, but it also does stuff on the server...

Waiting for page loads and AJAX requests to finish with Capybara

If you're using the Capybara webdriver, steps sometimes fail because the browser hasn't finished loading the next page yet, or it still has a pending AJAX request. You'll often see workarounds like

When I wait for the page to load
Then ...

Workarounds like this do not work reliably, will result in flickering tests and should be avoided. There is no known reliable way to detect if the browser has finished loading the page.

Solution

Instead you should wait until you can observe the result of a page load. E.g. if y...

Cucumber pitfall: "Around" does not apply to your "Background" steps

Around will not happen until after a feature's Background has been processed. Use Before and After to avoid that.

Details

Consider this Cucumber feature file:

Feature: Something that needs to be tested

  Background:
    Given a user
      And I sign in        
    
  Scenario: Sign out
    When I sign out
    Then I should see "Signed out"
  
  Scenario: Something else
    # ...

Now, assume you have these step definitions:

Around do
  puts "** Around: before yield"

...

Geordi: Running Selenium tests in a VNC buffer

Geordi now supports our solution for running Selenium tests without having Firefox or Chrome windows popping up all over your workspace.

This will stop Selenium windows from appearing on your desktop, but you can still inspect them when necessary.

Installation

Update geordi with gem install geordi.

Run geordi vnc --setup and follow the instructions.

Usage

geordi cucumber will automatically use VNC. Launchy will still open pages in the usual place.

geordi vnc will allow...

Cucumber step to interact with an iframe using Capybara webdriver

Give your iframe a name attribute (i.e. <iframe name="myframe">) and then simply use

When I press "Foo" in the iframe "myframe"
Then I should see "Bar!" in the iframe "myframe"

Step as follows:

Then /^(.*) in the iframe "([^\"]+)"$/ do |step, iframe_name|
  browser = page.driver.browser
  browser.switch_to.frame(iframe_name)
  step(step)
  browser.switch_to.default_content
end

Migrating to Spreewald

This describes how to migrate an existing cucumber test suite to Spreewald.

  1. Add the gem

  2. Include spreewald into your cucumber environment by putting
    require 'spreewald/web_steps'
    require 'spreewald/email_steps'
    # ...
    or just
    require 'spreewald/all_steps'
    into your support/env.rb.

  3. Look through your step definitions for everything that might be included in Spreewald. Candidates are web_steps, shared_steps, table_steps, `em...

Spreewald: Old-school cucumber steps, freshly pickled

Cucumber_rails' old-school web-steps have been deprecated for a while, urging developers to write high-level step definitions that directly use Capybara or Webrat.

We think that's a bit drastic. More high-level steps are good, but ticking the odd check box with a general step is not always bad.

So we took the old web steps, improved them a bit, added some other favorites of ours (steps for emails, tables, [time travelling](/ma...

Create autocompletion dropdown for Cucumber paths in Textmate

Ever wanted autocompletion for paths from paths.rb in Cucumber? This card lets you write your steps like this:

When I go to path *press tab now* # path is replaced with a list of all known Cucumber paths

This is how you do it

(key shortcuts apply for TextMate2)

  1. Open the bundle editor (ctrl + alt +  + B)

  2. Create a new Item ( + N), select "Command"

  3. Paste this:
    ^
    #!/usr/bin/env ruby -wKU
    require File.join(ENV['TM_SUPPORT_PATH'], 'lib', 'ui.rb')

    cucumber_paths = File.join ENV['TM_PROJECT_DIRECTORY'], 'features'...

Test meta-refresh redirects with Cucumber

The step definition below allows you to write:

Then I should see an HTML redirect to "http://www.makandracards.com" in the page head

Capybara

Then /^I should see an HTML redirect to "([^\"]*)" in the page head$/ do |redirect_url|
  page.should have_xpath("//meta[@http-equiv=\"refresh\" and contains(@content, \"#{redirect_url}\")]")
end

To find meta tags with Capybara, you can also use page.find('meta', visible: false).

Beware of magic adblocker rules

If a customer calls and tells you that she cannot see some content of her website beware of the following before starting tcpdump or other forensic debugging tools.

Some ad blockers like Adblock are shipped with filters that remove (amongst others)

  • divs named 'sponsor'
  • images with a class "banner" or the filename "banner.png"
  • images from directories named 'sponsor*'
  • Image names or class names that begin with adv_....

Renaming the div to something more specific to your site and renaming...

How to test print stylesheets with Cucumber and Capybara

A print stylesheet is easy to create. Choose a font suited for paper, hide some elements, done. Unfortunately print stylesheets often break as the application is developed further, because they are quickly forgotten and nobody bothers to check if their change breaks the print stylesheet.

This card describes how to write a simple Cucumber feature that tests some aspects of a print stylesheets. This way, the requirement of having a print stylesheet is manifested in your tests and cannot be inadvertedly removed from the code. Note that you can...