RSpec 3.5 has been released!

Sam Phippen, Myron Marston and Jon Rowe

Jul 1, 2016

RSpec 3.5 has just been released! Given our commitment to semantic versioning, this should be a trivial upgrade for anyone already using RSpec 3, but if we did introduce any regressions, please let us know, and we’ll get a patch release out with a fix ASAP.

RSpec continues to be a community-driven project with contributors from all over the world. This release includes over 600 commits and 150 merged pull requests from over 50 different contributors!

Thank you to everyone who helped make this release happen!

Notable Changes

Core: config.when_first_matching_example_defined

We generally advise that you avoid putting setup logic in spec_helper.rb that is needed by only some of your specs–that way, you can minimize the boot time to run isolated unit specs. Instead, that kind of setup logic can go in a file in spec/support. Spec files that need it can then require the support file and tag the example group to opt-in to any associated hooks and module inclusions, e.g.:

require 'support/db'

RSpec.describe SomeClassThatUsesTheDB, :db do
  # ...
end

This works, but it’s always felt sub-optimal that both the require and the :db tag are necessary to make this work. It’s duplication that happens in every spec file that uses the DB. If I forget to put the require 'support/db' line in a spec file that uses the DB, I can get in a situation where the spec file fails when run individually, but passes when run with the entire suite (because other spec files load the support file).

RSpec 3.5 includes a new hook that works nicely in this situation. Instead of requiring support/db in each spec file that needs it, you can configure RSpec to load it if any examples tagged with :db are defined:

RSpec.configure do |config|
  config.when_first_matching_example_defined(:db) do
    require 'support/db'
  end
end

This new when_first_matching_example_defined hook fires as soon as the first example with matching metadata is defined, allowing you to configure things to be loaded as needed based on metadata. Of course, this new hook isn’t limited to just this use case, but it’s one of the main ways we expect to see it used.

Core: config.filter_run_when_matching

One of the common uses for RSpec’s metadata system is focus filtering. Before RSpec 3.5, you’d configure it like this:

RSpec.configure do |config|
  config.filter_run :focus
  config.run_all_when_everything_filtered = true
end

Then you can tag an example or group with :focus to have RSpec run just what you’ve tagged. When nothing is tagged with :focus you want RSpec to ignore this filter, so the run_all_when_everything_filtered = true option makes it do that.

Unfortunately, run_all_when_everything_filtered applies globally to all filtering (not just :focus filtering), and it creates some surprising behavior in some situations. (See this issue for one example). We realized that it would make a lot more sense to be able to setup :focus as a conditional filter, so in RSpec 3.5 you can do that:

RSpec.configure do |config|
  config.filter_run_when_matching :focus
end

With this configuration, the :focus filtering will only apply if any examples or groups are tagged with :focus. It also makes for shorter, simpler configuration!

Core: Load spec files in order specified at command line

RSpec 3.5 now loads spec files and directories in the order of your command line arguments. This provides a simple way to order things in a one-off manner. For example, for a particular spec run if you want your fast unit specs to run before your slow acceptance specs, you can run RSpec like so:

$ rspec spec/unit spec/acceptance --order defined

The --order defined bit is only needed if you’ve configured RSpec to normally order things randomly (which we recommend as your default).

Core: Shared example group inclusion changes

RSpec has supported the idea of a shared context–a shared example group defined for the purpose of sharing contextual helpers and hooks–for a long time. You define a shared context like this:

RSpec.shared_context "DB support" do
  let(:db) { MyORM.database }

  # Wrap each example in a transaction...
  around do |ex|
    db.transaction(:rollback => :always, &ex)
  end

  # Interleave example begin/end messages in DB logs so it
  # is clear which SQL statements come from which examples.
  before do |ex|
    db.logger.info "Beginning example: #{ex.metadata[:full_description}"
  end
  after do |ex|
    db.logger.info "Ending example: #{ex.metadata[:full_description}"
  end
end

To use this shared context, you can explicitly include it in a group with include_context:

RSpec.describe MyModel do
  include_context "DB support"
end

We also supported a way of implicitly including the shared context in a group using matching metadata:

RSpec.shared_context "DB support", :db do
  # ...
end

# ...

RSpec.describe MyModel, :db do
  # ...
end

This approach worked OK, but had several significant problems:

In RSpec 3.5 we’ve rectified these problems with a couple of changes.

New API: config.include_context

You can now define shared context inclusions in your RSpec.configure block:

RSpec.configure do |config|
  config.include_context "DB support", :db
end

This aligns with the existing config.include API for module inclusions, provides a way to include shared contexts based on metadata that is less surprising, and makes it simple to include a shared context in all example groups (just don’t pass a metadata argument).

New config option: config.shared_context_metadata_behavior

We’ve also added a config option that lets you determine how shared context metadata is treated:

RSpec.configure do |config|
  config.shared_context_metadata_behavior = :trigger_inclusion
  # or
  config.shared_context_metadata_behavior = :apply_to_host_groups
end

The former value (:trigger_inclusion) is the default and exists only for backwards compatibility. It treats metadata passed to RSpec.shared_context exactly how it was treated in RSpec 3.4 and before: it triggers inclusion in groups with matching metadata. We plan to remove support for it in RSpec 4.

The latter value (:apply_to_host_groups) opts-in to the new behavior. Instead of triggering inclusion in groups with matching metadata, it applies the metadata to host groups. For example, you could focus on all groups that use the DB by tagging your shared context:

RSpec.shared_context "DB support", :focus do
  # ...
end

Expectations: Keyword argument support for the respond_to matcher.

Keyword arguments have been a stable language feature for some time now but RSpec has lacked support for placing expectations upon keyword arguments in a variety of our matchers.

In rspec-expectations 3.5 we add support for checking whether an object responds to a method using keyword arguments. You’ll now be able to check a method’s response signature for particular keyword(s) as well as a count of traditional arguments.

expect(my_object).to respond_to(:find).with_keywords(:limit, :offset)
expect(my_object).to respond_to(:find).with(1).argument.and_keywords(:limit, :offset)

We’re also expanding the matchers existing capabilities, adding the ability to check for a range of arguments, or unlimited arguments in addition to a specific number. e.g:

expect(my_object).to respond_to(:build).with(2..3).arguments
expect(my_object).to respond_to(:build).with_unlimited_arguments

A big thank you to Rob Smith for the work you put into making this a part of RSpec.

Expectations: Minitest integration now works with Minitest 5.6+

While rspec-expectations is normally used with rspec-core, you can easily use it with other test frameworks. We provide integration with Minitest. Simply load our Minitest support after loading Minitest itself:

require 'rspec/expectations/minitest_integration'

Unfortunately, Minitest 5.6 introduced its own expect method which conflicted with the expect method we provide and broke this integration. There’s a fix for this in rspec-expectations 3.5.

Mocks: Add Minitest integration

While we’ve long provided Minitest integration for rspec-expectations, we’ve never provided the same level of simple integration with rspec-mocks. Instead, users had to integrate rspec-mocks with Minitest themselves using the lifecycle hooks we provide. This worked pretty well until the aforementioned expect method was added to Minitest 5.6 and broke things for users trying to use rspec-mocks with minitest. In rspec-mocks 3.5, we now provide first-class support for usage with Minitest. Just require our integration file:

require 'rspec/mocks/minitest_integration'

Rails: Support for Rails 5

The headline here is that RSpec 3.5.0 is compatible with Rails 5. As Rails 5 betas and release candidates have been released, we’ve been releasing betas of 3.5.0 to keep up alongside Rails. Due to this being a major release of Rails some APIs that we consume have been deprecated. RSpec is not doing a major release and so this only gets exposed to you, our users, in one place: controller testing.

In Rails 5 assigns and assert_template are “soft deprecated”. Controller tests themselves are not, and adding :type => :controller to your specs is still 100% supported. Through Rails 3 and 4 it was both prevalent and idiomatic to use assigns in controller specs. As this is a minor release of RSpec our commitment to SemVer means that we are not going to break your existing controller specs. For existing Rails applications that make heavy use of assigns adding the rails-controller-testing to your Gemfile will restore assigns and assert_template. RSpec integrates with this gem seamlessly, so your controller specs should just continue to work.

For new Rails apps: we don’t recommend adding the rails-controller-testing gem to your application. The official recommendation of the Rails team and the RSpec core team is to write request specs instead. Request specs allow you to focus on a single controller action, but unlike controller tests involve the router, the middleware stack, and both rack requests and responses. This adds realism to the test that you are writing, and helps avoid many of the issues that are common in controller specs. In Rails 5, request specs are significantly faster than either request or controller specs were in rails 4, thanks to the work by Eileen Uchitelle[^foot_1] of the Rails Committer Team.

The other important feature of Rails 5 we wanted to discuss is ActionCable. Unfortunately RSpec is not able to provide a clean way of testing ActionCable at this time. Rails is working on a testing type for ActionCable slated for release as part of Rails 5.1. We’ll be watching that closely and work something up when it’s ready. In the mean time, we suggest you test ActionCable through a browser, in an integrated fashion.

The work on Rails 5 represented a significant investment by a number of RSpec Core Team members, and we received significant help from members of the Rails Commiter and Core teams. We offer kind thanks to everyone that was involved with making this possible.

Stats

Combined:

rspec-core:

rspec-expectations:

rspec-mocks:

rspec-rails:

rspec-support:

Docs

API Docs

Cucumber Features

Release Notes

RSpec Core (combining all betas of RSpec 3.5.0)

3.5.0 / 2016-07-01

Full Changelog

Enhancements:

Bug Fixes:

3.5.0.beta4 / 2016-06-05

Full Changelog

Enhancements:

Bug Fixes:

3.5.0.beta3 / 2016-04-02

Full Changelog

Enhancements:

Bug Fixes:

3.5.0.beta2 / 2016-03-10

Full Changelog

Enhancements:

3.5.0.beta1 / 2016-02-06

Full Changelog

Enhancements:

Bug Fixes:

RSpec Expectations (including all betas of RSpec 3.5.0)

3.5.0 / 2016-07-01

Full Changelog

No user facing changes since beta4

3.5.0.beta4 / 2016-06-05

Full Changelog

Bug Fixes:

3.5.0.beta3 / 2016-04-02

Full Changelog

Enhancements:

3.5.0.beta2 / 2016-03-10

Full Changelog

Enhancements:

Bug Fixes:

3.5.0.beta1 / 2016-02-06

Full Changelog

Enhancements:

Bug Fixes:

RSpec Mocks (including all betas of RSpec 3.5.0)

3.5.0 / 2016-07-01

Full Changelog

Enhancements:

3.5.0.beta4 / 2016-06-05

Full Changelog

Enhancements:

3.5.0.beta3 / 2016-04-02

Full Changelog

Enhancements:

3.5.0.beta2 / 2016-03-10

Full Changelog

Enhancements:

Bug Fixes:

3.5.0.beta1 / 2016-02-06

Full Changelog

Bug Fixes:

RSpec Support (including all betas of RSpec 3.5.0)

3.5.0 / 2016-07-01

Full Changelog

No user facing changes since beat4

3.5.0.beta4 / 2016-06-05

Full Changelog

Enhancements: * Improve MethodSignature to better support keyword arguments. (#250, Rob Smith).

3.5.0.beta3 / 2016-04-02

Full Changelog

Bug Fixes:

3.5.0.beta2 / 2016-03-10

Full Changelog

No user-facing changes.

3.5.0.beta1 / 2016-02-06

Full Changelog

Footnotes

[^foot_1]: See also Eileen’s talk about request spec performance