Test concurrent Ruby code

To test concurrent code, you will need to run multiple threads. Unfortunately, when you use blocking system calls (e.g. locks on the database), Ruby 1.8 threads won't work because system calls will block the whole interpreter.

Luckily you can use processes instead. fork spins off a new process, IO.pipe sends messages between processes, Process.exit! kills the current process. You will need to take care of ActiveRecord database connections.

Here is a full-fledged example:

describe Lock, '.acquire' do

  before :each do
  ...

Check if two arrays contain the same elements in Ruby, RSpec or Test::Unit

RSpec 1, RSpec 2

To test whether two arrays have the same elements regardless of order, RSpec 1 and 2 give you the =~ matcher:

actual_array.should =~ expected_array

Rspec 3

With RSpec 3's expect syntax you can choose one of these two matchers:

expect(actual_array).to match_array(['1', '2', '3'])
expect(actual_array).to contain_exactly('1', '2', '3')

Note how match_array takes an argument, but contain_exactly takes a list of elements as varargs.

Test::Unit

If y...

Test that a number or money amount is shown with Cucumber

This is an awful way to test whether a number is shown on the screen:

Then I should see "5"

It is awful because the step above is green for 5, 5123 and -51.

This step definition below makes sure this doesn't happen. You can use it like this:

Then I should see the number 5

The step also works if you you'd like to test that the number is followed by a unit:

Then I should see the amount 5 €

The separator between the number and its unit is allowed to be either a space or a [nbsp](https://makandracards.com/makandra/838-generate...

Concurrent Tests

Install gem and plugin

sudo gem install parallel
script/plugin install git://github.com/grosser/parallel_tests.git

Adapt config/database.yml

test:
  database: xxx_test<%= ENV['TEST_ENV_NUMBER'] %>

Create test databases

script/dbconsole -p
CREATE DATABASE `xxx_test2`;
...

Generate RSpec files

script/generate rspec

(you'll probably only let it overwrite files in script/)

Prepare test databases...

Rails 2's CookieStore produces invalid cookie data, causing tests to break

Note that this seems to affect only recent Rails 2 versions.

You will not encounter this until you are writing to the cookie more than once, but when doing so, integration tests (Cucumber) may break for you with this error:

You have a nil object when you didn't expect it!
You might have expected an instance of ActiveRecord::Base.
The error occurred while evaluating nil.[] (NoMethodError)

Background

The regular/short cucumber backtrace is not of any help but looking at the full trace reveals that ActionPack's `actio...

Test downstream bandwidth of Internet connection

You want to test your 1GE or 10GE internet uplink? We needed to ensure we have full 10GE to the backbone for a customer project.

Using netcat

To test whether we can achieve the bandwidth internally, you can use netcat and dd like this:

On your first server: nc -v -l 55333 > /dev/null
On your second server: dd if=/dev/zero bs=1024K count=5000 | nc -v $remote_ip 55333

You should see some output like this:

user@xxx:~ % dd if=/dev/zero bs=1024K count=5000 | nc -v removed 55333
Connection to 91.250.95.249 55333 port [...

Cucumber: How to avoid VCR errors for unused requests in pending tests

When you have a pending Cucumber step (or feature) that also uses an existing VCR cassette, your pending test may fail at the very end with an error like this:

There are unused HTTP interactions left in the cassette:
  - [get ...] => [200 ...]
  - [get ...] => [200 ...] (VCR::Errors::UnusedHTTPInteractionError)

The error happens because your VCR is configured to complain about cassettes that contain extra requests which your test did not use. This is often a good configuration.
If you do not want to change your whole test suite...

Mocks and stubs in Test::Unit when you are used to RSpec

We are maintaining some vintage projects with tests written in Test::Unit instead of RSpec. Mocks and stubs are not features of Test::Unit, but you can use the Mocha gem to add those facilities.

The following is a quick crash course to using mocks and stubs in Mocha, written for RSpec users:

|---------------------------------------------------------|
| RSpec | Mocha |
|---------------------------------------------------------|
| obj = double() | obj = mock() |
| obj.stub(:method => 'value') | `obj.stubs...

chromedriver-helper gem in Gemfile might break you selenium tests (of other projects)

Summary: Don't add chromedriver-helper to the Gemfile

  • the executables might break your tests in projects where chromedriver-helper is not in the Gemfile
  • developers with different chrome versions will have problems using the same chromedriver-helper version

Background

If you install the chromedriver-helper gem, but don't have it in you Gemfile, your selenium tests might fail with:

Selenium::WebDriver::Error::WebDriverError: unable to connect to chromedriver 127.0.0.1:9515

The reason is that chromedriver-helper ov...

How to test bundled applications using Aruba and Cucumber

Aruba is an extension to Cucumber that helps integration-testing command line tools.

When your tests involve a Rails test application, your tool's Bundler environment will shadow that of the test application. To fix this, just call unset_bundler_env_vars in a Cucumber Before block.

Previously suggested solution

Put the snippet below into your tool's features/support/env.rb -- now any command run through Aruba (e.g. via #run_simple) will have a clean Bundler envir...

Jasmine: Test that an object is an instance of a given class

To test that an object was constructed by a given constructor function, use jasmine.any(Klass):

describe('plus()', function() {
  it ('returns a number', function() {
    let result = plus(1, 2)
    expect(result).toEqual(jasmine.any(Number))
  })
})

Also see Expecting objects as method invocation arguments.

Enable CSRF protection in Javascript tests

You might not know that Rails disables CSRF protection in tests. This means that if you accidentally forget to send the CSRF token for non-GET requests, your tests will be green even though your application is completely broken (a failed CSRF check usually logs out the user). Rails probably does this because CSRF protection sort of requires Javascript.

You want to enable CSRF protection in Cucumber scenarios that can speak Javascript. To do so, copy the a...

Set the accept-language of Chrome in selenium tests

You can set the resolution and user agent used in selenium tests with chrome with the method described in this card, but you can also set the accept-language and other profile settings if you do this:

# features/support/chrome.rb
require "selenium/webdriver"


Capybara.register_driver :chrome320x480 do |app|
  args = []
  args << "--window-...

Test whether Perfect Forward Secrecy (PFS) is enabled on a server (using OpenSSL)

Use the following command to test if a server (in this example: makandra.com on port 443) uses Perfect Forward Secrecy (PFS):

openssl s_client -connect makandra.com:443 -cipher ECDHE-RSA-RC4-SHA

You should see something like the following:

~ > openssl s_client -connect projecthero.com:443 -cipher ECDHE-RSA-RC4-SHA
CONNECTED(00000003)
depth=1 O = AlphaSSL, CN = AlphaSSL CA - G2
verify error:num=20:unable to get local issuer certificate
verify return:0
---
Certificate chain
 0 s:/C=DE/OU=Domain Control Va...

Shoulda Matchers: how to test conditional validations

Shoulda Matchers don't provide canditional validations (validations with if: option). Here is how to write tests for the condition:

Class:

class Employee < ActiveRecored::Base
  validates :office, presence: true, if: manager?
  
  def manager?
    ...
  end
end

Test:

describe Employee do

  describe '#office' do
    
    context 'is a manager' do
      before { allow(subject).to receive(:manager?).and_return(true) }
      it { is_expected.to validate_presence_o...

Test that a select field contains an option with Cucumber

This note describes a Cucumber step definition that lets you say:

Then "Mow lawn" should be an option for "Activity"
But "Reply support mail" should not be an option for "Activity"

Note that this step checks whether an option is available, not that it is selected. There is a separate step to test that an option is selected.

Capybara (0.4.1 or higher)

Then /^"([^"]*)" should( not)? be an option for "([^"]*)"(?: within "([^\...

How to send a test e-mail from shell

If you want to manually check if e-mail delivery works on a machine by sending an e-mail you can run the following:
mail -s Test someone@example.com < /dev/null

This will send an empty e-mail with "Test" as its subject to someone@example.com.

If you want it to contain a message body, call mail -s Test someone@example.com only; the mail application will then read your input from stdin. Finish your message by sending EOT with Ctrl-D -- if you are asked for anything else ...

Namespacing: why `uninitialized constant` error may occour in `development` but not in `test` environment

Example:

  class Book::Page
  end
  
  class MyBook < Book
    def new_page
      Page.new # has to be `Book::Page` in development to make it work 
    end
  end

Method new_page may throw an error when it was called during browser interaction in development but doesn't make the test fail.

The reason

Development autoloading isn't smart enough to find the referenced class
At other environments (test, staging, production) autoloading is disabled, that all classes are already loaded when browser interaction takes place what makes...

Aggregated RSpec/Cucumber test coverage with RCov

With defaults, RCov doesn't work the way you how you would like it to. To create a nice test coverage report, copy the attached file to lib/tasks/rcov.rake. After that rake rcov:all will run all RSpec examples and Cucumber features. The report will be written RAILS_ROOT/coverage/index.html.

Here is what the task does in detail:

  • Generates aggregated coverage of both RSpec and Cucumber
  • Works with Rails 2 and Rails 3
  • Reports for app/**/*.rb and nothing else
  • If called with an environment variable IGNORE_SHARED_TRAITS=true it ...

RSpec expects pending tests to fail

When flagging a spec that will be implemented later as pending, include a failing spec body or RSpec 3 will consider the pending test as failing.

The reasoning is: If the spec is flagged as pending but passes, it should not be pending. So these will fail:

it 'does something' do
  pending
end

it 'does something else' do
  pending
  expect(1).to eq(1)
end

The first case may be unexpected, if you just wanted to write down that something should eventually happen that will be implemented later.

Instead, ...

On Being A Journeyman Software Craftsman: Test-first and Test-driven conversation with JB Rainsberger

We had a conversation about the fact that the 'TDD is about testing vs TDD is about design" debate that keeps popping up, especially now in the Ruby community.

Test that a CSS selector is present with Cucumber

This note describes a Cucumber step definition that lets you test whether or not a CSS selector is present on the site:

Then I should see an element "#sign_in"
But I should not see an element "#sign_out"

Here is the step definition for Capybara:

Then /^I should (not )?see an element "([^"]*)"$/ do |negate, selector|
  expectation = negate ? :should_not : :should
  page.send(expectation, have_css(selector))
end

Here is the step definition for Webrat:

Then /^I should (not )?see an element "([^"]*)"$/ do |negate...

Cucumber: Test that an element is not overshadowed by another element

I needed to make sure that an element is visible and not overshadowed by an element that has a higher z-index (like a modal overlay).

Here is the step I wanted:

Then the very important notice should not be overshadowed by another element

This is the step definition:

Then(/^(.*?) should not be overshadowed by another element$/) do |locator|
  selector = selector_for(locator)
  expect(page).to have_css(selector)
  js = <<-JS
    var selector = #{selector.to_json};
    var elementFromSelector = document.querySelector(selector)...

Why you see a GET "/__identify__" request in Capybara tests

You might wonder about this request in your test.log:

Started GET "/__identify__" for 127.0.0.1 at 2015-04-29 18:00:02 +0100

This is what happens: For drivers like Selenium, Capybara will boot up a Thin or Webrick server in a separate thread. It then makes a GET request to /__identify__ to see if the server is ready to accept requests.

Since you don't have a route that responds to /__identify, Capybara will wrap your Rails app in...