RSpec 2.14 is released!

Myron Marston

Jul 8, 2013

We've just released RSpec 2.14. It will be the last 2.x feature release and is a recommended upgrade for all users. We're getting started on RSpec 3. I'll be blogging about our plans for RSpec 3 next week, so check back soon :).

Thanks to all the contributors who helped make this RSpec release happen.

Notable New Features

Core: Profiler now profiles example groups, too

RSpec has long had the --profile option for dumping the top N slowest examples at the end. In RSpec 2.14, this has been enhanced so that it prints the groups with the largest mean example time. For example, here's the output from using --profile 5 on rspec-core's suite:

Top 5 slowest examples (0.38945 seconds, 10.8% of total time):
  RSpec::Core::Formatters::TextMateFormatter produces HTML identical to the one we designed manually
    0.10471 seconds ./spec/rspec/core/formatters/text_mate_formatter_spec.rb:64
  ::DRbCommandLine --drb-port without RSPEC_DRB environment variable set sets the DRb port
    0.07461 seconds ./spec/rspec/core/drb_command_line_spec.rb:39
  RSpec::Core::Runner#run with --drb or -X and a DRb server is running builds a DRbCommandLine and runs the specs
    0.0744 seconds ./spec/rspec/core/runner_spec.rb:47
  command line when a custom order is configured orders the groups and examples by the provided strategy
    0.06924 seconds ./spec/command_line/order_spec.rb:169
  RSpec::Core::ConfigurationOptions#configure sends pattern before files_or_directories_to_run
    0.06649 seconds ./spec/rspec/core/configuration_options_spec.rb:48

Top 5 slowest example groups:
  RSpec::Core::Formatters::HtmlFormatter
    0.05861 seconds average (0.05861 seconds / 1 example) ./spec/rspec/core/formatters/html_formatter_spec.rb:9
  RSpec::Core::Formatters::TextMateFormatter
    0.05713 seconds average (0.1714 seconds / 3 examples) ./spec/rspec/core/formatters/text_mate_formatter_spec.rb:9
  command line
    0.05218 seconds average (0.31307 seconds / 6 examples) ./spec/command_line/order_spec.rb:3
  ::DRbCommandLine
    0.02016 seconds average (0.16127 seconds / 8 examples) ./spec/rspec/core/drb_command_line_spec.rb:4
  RSpec::Core::Runner
    0.01822 seconds average (0.10931 seconds / 6 examples) ./spec/rspec/core/runner_spec.rb:5

Core: New --warnings flag to enable Ruby's warning mode

You can now pass the --warnings or -w flag to enable ruby's warning mode.

Core: Shared example groups are scoped to the context they are defined in

Before 2.14, shared example groups were stored in a global hash, could be defined in any context, and could be used from any context. In 2.14, this has changed: shared example groups are now scoped to the context they are defined in. That means you can now do this:

describe MySinatraApp1 do
  shared_examples_for "error handling" do
    # some examples would go here
  end

  context 'GET to /foo' do
    include_examples "error handling"
  end

  context 'GET to /bar' do
    include_examples "error handling"
  end
end
describe MySinatraApp2 do
  shared_examples_for "error handling" do
    # some different examples would go here
  end

  context 'GET to /foo' do
    include_examples "error handling"
  end

  context 'GET to /bar' do
    include_examples "error handling"
  end
end

Here there are two different "error handling" shared example groups, each scoped to (and used from) a different example group. As demonstrated here, shared example groups are available from the context they are defined in or from any nested context. In 2.14, shared example groups declared in sibling contexts are still available to maintain backwards compatibility, but will print a deprecation warning. In 3.0, you will not be able to use a shared example group that was defined in a sibling context.

Core: Deprecation output now configurable

RSpec 2.14 has deprecated a number of things in preparation for removal in 3.0. To help reduce the noisiness of the increased number of deprecations, there's a new option that can direct deprecation warnings to a file:

# spec_helper.rb
RSpec.configure do |rspec|
  rspec.deprecation_stream = 'log/deprecations.log'
  # or
  rspec.deprecation_stream = File.open("/path/to/file", "w")
end

Normally, deprecation warnings get printed to stderr. When you configure this, it'll send deprecation warnings to the configured file instead, and at the end of the spec run it will print out a message like:

2 deprecations logged to log/deprecations.log

Mocks: New message expectation syntax

In RSpec 2.11, we added a new syntax to rspec-expectations that removes a reliance on monkey-patching, avoiding certain kinds of problems related to proxy objects. In RSpec 2.14, we've extended that same syntax to rspec-mocks:

mailer = double("Mailer")

# old syntax:
mailer.stub(:deliver_welcome_email)
mailer.should_receive(:deliver_welcome_email).with(an_instance_of(User))

# new syntax
allow(mailer).to receive(:deliver_welcome_email)
expect(mailer).to receive(:deliver_welcome_email).with(an_instance_of(User))

For more details, read Sam Phippen's announcement blog post from the 2.14.0.rc1 release.

Mocks: Spies

Joe Ferris from Thoughtbot implemented this new feature. Traditionally, rspec-mocks has required that you set a message expectation before the message is received:

mailer = double("Mailer")
expect(mailer).to receive(:deliver_welcome_email).with(an_instance_of(User))
UserCreationService.new(mailer).create_user(params)

In some situations, this ordering feels backwards (particularly if you try to organize your tests using an arrange/act/assert pattern). Spies allow you to assert that a message was received after the fact:

mailer = double("Mailer", deliver_welcome_email: nil)
UserCreationService.new(mailer).create_user(params)
expect(mailer).to have_received(:deliver_welcome_email).with(an_instance_of(User))

Note that you first have to stub the message you will later expect so that rspec-mocks can spy on it. (There's no feasible performant way for rspec-mocks to automatically spy on all method calls). Alternately, you can create your test double as a null object, (using double().as_null_object) which has the effect of auto-spying on all messages sent to that object:

mailer = double("Mailer").as_null_object
UserCreationService.new(mailer).create_user(params)
expect(mailer).to have_received(:deliver_welcome_email).with(an_instance_of(User))

Docs

RDoc

Cucumber Features

Release Notes

rspec-core 2.14.0

Full Changelog

Enhancements:

Bug fixes:

Deprecations

rspec-expectations 2.14.0

Full Changelog

Enhancements:

Bug fixes:

Deprecations

rspec-mocks 2.14.0

Full Changelog

Enhancements:

Bug Fixes:

Deprecations:

rspec-rails 2.14.0

Enhancements:

Bug fixes: