TL;DR

Explicit use of the “subject” abstraction is a code smell, and should be refactored to use a more intention revealing name whenever possible.

One liners

rspec-core supports a one-liner syntax to reduce the noise of common requirements like validations:

Without support for this syntax, the same example might look like this:

The benefit of this more verbose example is that it we can read it and understand all the parts right away: an object is initialized and assigned to a local variable, then that variable is used for an expectation.

The benefit of the one-liner is that it’s terse and expressive, but that comes at a cost: you can’t see what the expectation is being evaluated against, so you have to understand some underlying mechanics in order to isolate/understand a failure.

The subject abstraction

Behind the scenes, the one-liner uses a “subject” abstraction supported by two methods named subject. One is a class method on ExampleGroup, used to declare the “subject” of all of the examples in the group:

The other is an instance method on ExampleGroup. The first time it is called within an example the block passed to the class’ subject method is evaluated and its result memoized, returning the same value from that and each subsequent subject call:

Here is what they look like together:

The problem with this example is that the word “subject” is not very intention revealing. That might not appear problematic in this small example because you can see the declaration on line 3 and the reference on line 6. But when this group grows to where you have to scroll up from the reference to find the declaration, the generic nature of the word “subject” becomes a hinderance to understanding and slows you down.

In this case, we’d be better served by using a method (or a let declaration) with an intention revealing name:

If we can do that, you might wonder why we have “subject” at all. Well, it was originally designed to never be seen:

Implicit subject

Note in the example with subject { Article.new }, that the subject declaration is initializing an Article with no args. Since RSpec knows that the first argument to describe is the Article class, it can store a similar block in the background as a default, implicit subject declaration, leaving us with:

That’s a little better, but now subject appears out of nowhere in the example, leaving the reader to wonder where it came from. To remove the need for explicitly referencing subject, the example delegates should and should_not to subject when it is, itself, the receiver:

Starting that line with “should” seems a bit odd though, so the final step is to do it all in one line:

Now we have a completely implicit subject, and the result reads quite nicely in both the code and the output when run with --format documentation:

Article
  should validate presence of :title

We still need to trust that something is doing some work for us but it’s all operating at the same level of abstraction, so we don’t have to try to interpret half of the functionality.

Avoid explicit use of subject

Intention revealing names are crucial if you want to be able to quickly scan and understand code as you navigate around different parts of a system. This is as true for specs as it is for implementation, and the generic nature of the word “subject” makes it a poor choice when a more intention revealing name can be used.

Is an explicit subject ever OK?

Guidelines are guidelines; YMMV. In general I would recommend that if there is a reasonable way to use an intention revealing name instead of “subject”, you should. The only use case I can think of in RSpec in which another name can’t be used is the one liner syntax:

Here we have to use subject because that’s the only way to tell RSpec where to send should and should_not. In my opinion, any other explicit appearance of subject can and should be refactored to use an intention revealing name.

Update 2012-05-15

Based on feedback on this post, I added support for a “named subject,” which lets you reference the declared subject implicitly in one-liners and with an intention revealing name in standard examples:

This will be released with rspec-core-2.11. Keep your eyes out for it!

These are patch releases recommended for anybody who has already upgraded to 2.10.

rspec-mocks-2.10.1

full changelog

Bug fixes

  • fix regression of edge case behavior
    • fixed failure of object.should_receive(:message).at_least(0).times.and_return value
    • fixed failure of object.should_not_receive(:message).and_return value

rspec-rails-2.10.1

full changelog

Bug fixes

rspec-2.10 is released!

May 3rd, 2012

API Docs (RDoc)

http://rubydoc.info/gems/rspec-core
http://rubydoc.info/gems/rspec-expectations
http://rubydoc.info/gems/rspec-mocks
http://rubydoc.info/gems/rspec-rails

Cucumber docs

http://relishapp.com/rspec/rspec-core
http://relishapp.com/rspec/rspec-expectations
http://relishapp.com/rspec/rspec-mocks
http://relishapp.com/rspec/rspec-rails

rspec-core-2.10.0

full changelog

Enhancements

  • Add prepend_before and append_after hooks (preethiramdev)
    • intended for extension libs
    • restores rspec-1 behavior
  • Reporting of profiled examples (moro)
    • Report the total amount of time taken for the top slowest examples.
    • Report what percentage the slowest examples took from the total runtime.

Bug fixes

  • Properly parse SPEC_OPTS options.
  • example.description returns the location of the example if there is no explicit description or matcher-generated description.
  • RDoc fixes (Grzegorz Świrski)
  • Do not modify example ancestry when dumping errors (Michael Grosser)

rspec-expectations-2.10.0

full changelog

Enhancements

  • Add new start_with and end_with matchers (Jeremy Wadsack)
  • Add new matchers for specifying yields (Myron Marson):
    • expect {...}.to yield_control
    • expect {...}.to yield_with_args(1, 2, 3)
    • expect {...}.to yield_with_no_args
    • expect {...}.to yield_successive_args(1, 2, 3)
  • match_unless_raises takes multiple exception args

Bug fixes

  • Fix be_within matcher to be inclusive of delta.
  • Fix message-specific specs to pass on Rubinius (John Firebaugh)

rspec-mocks-2.10.0

full changelog

Bug fixes

  • fail fast when an exactly or at_most expectation is exceeded

rspec-rails-2.10.0

full changelog

Bug fixes

  • render_views called in a spec can now override the config setting. (martinsvalin)
  • Fix render_views for anonymous controllers on 1.8.7. (hudge, mudge)
  • Eliminate use of deprecated process_view_paths
  • Fix false negatives when using route_to matcher with should_not
  • controller is no longer nil in config.before hooks
  • Change request.path_parameters keys to symbols to match real Rails environment (Nathan Broadbent)
  • Silence deprecation warnings in pre-2.9 generated view specs (Jonathan del Strother)

This is a bug-fix only release, and is recommended for everybody using rspec-2.9.

full changelog

Bug fixes

  • Provide a helpful message if the diff between two objects is empty.
  • Fix bug diffing single strings with multiline strings.
  • Fix for error with using custom matchers inside other custom matchers (mirasrael)
  • Fix using execution context methods in nested DSL matchers (mirasrael)

rspec-2.9.0 is released!

March 17th, 2012

rspec-2.9.0 is released wtih lots of bug fixes and a few minor feature improvements as well. Enjoy!

rspec-core-2.9.0 / 2012-03-17

full changelog

Enhancements

  • Support for “X minutes X seconds” spec run duration in formatter. (uzzz)
  • Strip whitespace from group and example names in doc formatter.
  • Removed spork-0.9 shim. If you’re using spork-0.8.x, you’ll need to upgrade to 0.9.0.

Bug fixes

  • Restore --full_backtrace option
  • Ensure that values passed to config.filter_run are respected when running over DRb (using spork).
  • Ensure shared example groups are reset after a run (as example groups are).
  • Remove rescue false from calls to filters represented as Procs
  • Ensure described_class gets the closest constant (pyromaniac)
  • In “autorun”, don’t run the specs in the at_exit hook if there was an exception (most likely due to a SyntaxError). (sunaku)
  • Don’t extend groups with modules already used to extend ancestor groups.
  • its correctly memoizes nil or false values (Yamada Masaki)

rspec-expectations-2.9.0 / 2012-03-17

full changelog

Enhancements

  • Move built-in matcher classes to RSpec::Matchers::BuiltIn to reduce pollution of RSpec::Matchers (which is included in every example).
  • Autoload files with matcher classes to improve load time.

Bug fixes

  • Align respond_to? and method_missing in DSL-defined matchers.
  • Clear out user-defined instance variables between invocations of DSL-defined matchers.
  • Dup the instance of a DSL generated matcher so its state is not changed by subsequent invocations.
  • Treat expected args consistently across positive and negative expectations (thanks to Ralf Kistner for the heads up)

rspec-mocks-2.9.0 / 2012-03-17

full changelog

Enhancements

  • Support order constraints across objects (preethiramdev)

Bug fixes

  • Allow a as_null_object to be passed to with
  • Pass proc to block passed to stub (Aubrey Rhodes)
  • Initialize child message expectation args to match any args (#109 - preethiramdev)

rspec-rails-2.9.0 / 2012-03-17

full changelog

Enhancments

  • add description method to RouteToMatcher (John Wulff)
  • Run “db:test:clone_structure” instead of “db:test:prepare” if Active Record’s schema format is “:sql”. (Andrey Voronkov)

Bug fixes

  • mock_model(XXX).as_null_object.unknown_method returns self again
  • Generated view specs use different IDs for each attribute.

TL;DR:

  • TDD is about specifying behavior, not structure.
  • Validations are behavior, and should be specified.
  • Associations are structure, and need not be.

Disclaimer

This is my personal viewpoint, though it is not mine alone. YMMV.

Declarations

ActiveRecord provides a declarative interface for describing the structure and behavior of a model:

While syntactically similar, these two declarations do fundamentally different things.

Validations are behavior

The validates_presence_of :title declaration changes the behavior of the save method (and other methods that use save), and should be specified explicitly. Here’s an example using shoulda matchers:

Even though the matcher’s name looks just like the likely implementation, the validate_presence_of matcher specifies that you can not save an Article without a non-nil value for title, not that the validates_presence_of(:title) declaration exists.

Associations are structure

The has_many declaration exposes a comments method to clients that appears to be a collection of Comment objects. Doing Test-Driven Development, you would add this declaration when a specified behavior requires it e.g.

This example needs a comments method that returns a collection in order to pass. If it doesn’t exist already (because no other example drove you to add it), this would be all the motivation you need to introduce it. You don’t need an example that says it "should have_many(:comments)".

Testing the framework

Some will argue that we don’t need to spec validations either, suggesting that it "should validate_presence_of(:title)" is testing the Rails framework, which we trust is already tested. If you think of TDD as a combination of specification, documentation, and regression testing, then this argument falls short on the specification/documentation front because the validation is behavior and, thus, the spec should specify the validation.

Even if you view testing as nothing more than a safety net against regressions, the argument still falls down in the face of refactoring. If we add a Review class that also has_many(:comments) and validates_presence_of(:title), and we want to extract that behavior to a Postable module that gets included in both Article and Review, we’d want a regression test to fail if we failed to include either of those declarations in the Postable module.

But declarations are already declarative!

Another argument is that declarations supply sufficient documentation. e.g. we can look at rental_contract.rb and know that it validates the presence of :rentable:

This is an interesting argument that I think has some merit, but I think it would require an extraordinarily disciplined and consistent approach of using declarations 100% of the time in model files such that each one is the spec for that model, e.g.

100% may sound extreme, but as soon as we define a single method body in any one of the models, the declarative nature of the file begins to degrade, and so does its fitness for the purpose of specification. Plus, if we can only understand the expected behavior of a model by looking at both its spec and its implementation, we’ve lost some of the power of a test-driven approach.

What do you think?

Do you spec associations? If so, what value do you get from doing so? If not, have you run into situations where you wished you had?

Same questions for validations.

rspec-rails-2.8.1 is released

January 5th, 2012

Bug fix release

The rails-3.2.0.rc2 release broke stub_model in rspec-rails-2.0.0 > 2.8.0. The rspec-rails-2.8.1 release fixes this issue, but it means that when you upgrade to rails-3.2.0.rc2 or greater, you’ll have to upgrade to rspec-rails-2.8.1 or greater.

Because rspec-rails-2.8.1 supports all versions of rails since 3.0, I recommend that you upgrade to rspec-rails-2.8.1 first, and then upgrade to rails-3.2.0.rc2 (or 3.2.0 once it’s out).

Changelog

http://rubydoc.info/gems/rspec-rails/file/Changelog.md

Docs

http://rubydoc.info/gems/rspec-rails
http://relishapp.com/rspec/rspec-rails

RSpec-2.8 is released!

January 4th, 2012

We released RSpec-2.8.0 today with a host of new features and improvements since 2.7. Some of the highlights are described below, but you can see the full changelogs at:

Documentation

While not 100% complete yet, we’ve made great strides on RSpec’s RDoc:

http://rspec.info is now just a one pager (desperate for some design love - volunteers please email rspec-users@rubyforge.org). All the old pages are redirects to the relevant RDoc at http://rubydoc.info. RSpec-1 info is still available at http://old.rspec.info.

We’ve still got Cucumber features up at http://relishapp.com/rspec, but we’re going to be phasing that out as the primary source of documentation. There are a lot of reasons for this, and I’ll try to follow up with a separate blog post on this topic.

rspec-core

Improved support for tags and filtering

You can now set default tags/filters in either RSpec.configure or a .rspec file and override these tags on the command line. For example, this configuration tells rspec to run all the examples that are not tagged :slow:

# in spec/spec_helper.rb
RSpec.configure do |c|
  c.treat_symbols_as_metadata_keys_with_true_values = true
  c.filter_run_excluding :slow
end

Now when you want run those, you can just do this:

rspec --tag slow

This will override the configuration and run onlly the examples tagged :slow.

–order rand

We added an --order option with two supported values: rand and default.

rspec --order random (or rand) tells RSpec to run the groups in a random order, and then run the examples within each group in random order. We implemented it this way (rather than complete randomization of every example) because we don’t want to re-run expensive before(:all) hooks. A fair tradeoff, as the resulting randomization is just as effective at exposing order-dependency bugs.

When you use --order random, RSpec prints out the random number it used to seed the randomizer. When you think you’ve found an order-dependency bug, you can pass the seed along and the order will remain consistent:

--order rand:3455

--order default tells RSpec to load groups and examples as they are declared in each file.

rspec –init

We added an --init switch to the rspec command to generate a “spec” directory, and “.rspec” and “spec/spec_helper.rb” files with some starter code in them.

rspec-expectations

We discovered that the matcher DSL generates matchers that run considerably slower than classes which implement the matcher protocol. We made some minor improvements in the DSL, but to really improve things we re-implemented every single built-in matcher as a class.

rspec-2.8.0.rc1 is released

November 6th, 2011

I just released rspec-2.8.0.rc1, which includes releases of rspec-core, rspec-expectations, rspec-mocks, and rspec-rails. Changelogs for each are at:

What’s new

Nothing really changed in rspec-rails or rspec-mocks, but rspec-core and rspec-expectations have both gotten some nice improvements.

Configuration (rspec-core)

rspec-core offers a number of configuration options which can be declared on the command line, in a config file (.rspec, ~/.rspec, or custom location), as well as in an RSpec.configure block (in spec/spec_helper.rb by convention). Before this release, some options, but not all, could be stored in config files and then overridden on the command line. The problems were that it was inconsistent (not all options worked this way), and we couldn’t override options that were set in RSpec.configure blocks.

With this release, almost all options declared in RSpec.configure can be overridden from the command line, and --tag options can override their inverses. For example, if you have this in .rspec:

--tag ~slow:true

That means “exclude examples tagged :slow => true“. So the following example would be excluded:

it "does something", :slow => true do
  # ...
end

You can also exclude that example from RSpec.configure like this:

RSpec.configure do |c|
  c.filter_run_excluding :slow => true
end

Note: the naming is different for historical reasons, and we will reconcile that in a future release, but for now, just know that --tag on the command line and in .rspec is synonymous with filter_run_[including|excluding] in RSpec.configure.

Override from command line

Whether the default is stored in .rspec or RSpec.configure, it can be overridden from the command line like this:

rspec --tag slow:true

“Profiles” in custom options files

The rspec command has an --options option that let’s store command line args in arbitrary files and tell RSpec where to find them. For example, you could set things up so your normal spec run excludes the groups and examples marked :slow by putting this in .rspec:

--tag ~slow

Now add a .slow file with:

--tag slow

Now run rspec to run everything but the slow specs, and run rspec --options .slow or rspec -O.slow to run the slow ones.

Override from Rake task

RSpec’s Rake task supports an rspec_opts config option, which means you can set up different groupings from rake tasks as well. The fast/slow example above would look like this:

namespace :spec do
  desc "runs the fast specs"
  RSpec::Core::RakeTask.new(:fast) do |t|
    t.rspec_opts = '--options .fast'
  end
  RSpec::Core::RakeTask.new(:slow) do |t|
    t.rspec_opts = '--options .slow'
  end
end

Or ..

namespace :spec do
  desc "runs the fast specs"
  RSpec::Core::RakeTask.new(:fast) do |t|
    t.rspec_opts = '--tag ~slow'
  end
  RSpec::Core::RakeTask.new(:slow) do |t|
    t.rspec_opts = '--tag slow'
  end
end

Implicit true value for tags/filters

This is not new in rspec-2.8, but all the tags/filters in the example above can be written without explicitly typing true:

--tag slow
--tag ~slow
RSpec.configure {|c| c.filter_run_excluding :slow}
 
it "does something", :slow do

You have to set a config option to enable this in rspec-2.x:

RSpec.configure {|c| c.treat_symbols_as_metadata_keys_with_true_values = true}

In rspec-3.0, this will be the default, but without setting this value in 2.x you’ll get a deprecation warning when you try to configure things this way. It’s ugly, I know, but this enabled us to introduce the new behavior without breaking compatibility with some suites in a minor release.

Ordering

With 2.8, you can now run the examples in random order, using the new --order option:

--order rand

The order is randomized with some reasonable caveats:

  • top level example groups are randomized
  • nested groups are randomized within their parent group
  • examples are randomized within their group

This provides a very useful level of randomization while maintaining the integrity of before/after hooks, subject, let, etc.

If you want to run the examples in the default ordering (file-system load order for files and declaration order for groups/examples), you can override the order from the command line:

--order default

Pseudo-randomization

The randomization is managed by Ruby’s pseudo-randomization. This means that if you find an order dependency and want to debug/fix it, you can fix the order by providing the same seed for each run:

--order rand:1234

The seed is printed to the console with each run, so you can just copy it to the command. You can also just specify the seed, which RSpec will assume means you want to run with --order rand:

--seed 1234

Every time you run the suite with the same seed, the examples will run in the same “random” order.

Built-in matchers are all classes in rspec-expectations

The Matcher DSL in rspec-expectations makes it dead simple to define custom matchers that suit your domain. The problem is that it is several times slower than defining a class to do so. While this doesn’t make much difference when you have a custom matcher that you use a few dozen times (where talking hundredths of seconds here), it does make a difference if every single matcher invocation in your entire suite suffers this problem.

The short term fix is that all of the built-in matchers have been re-implemented as classes rather than using the DSL to declare them. This has the added benefit of making it easier to navigate the code and RDoc

Longer term, we’ll try to refactor the internals of the matcher DSL so that it generates a class at declaration time. Eventually.

Summing up

So that’s it. Nothing ground breaking. Nothing compatibility breaking. But some nice new features and improvements that will make your life just a little bit nicer when you upgrade. We’re doing a release candidate because enough changed internally that I want to give you time to try it out, so please, please do so! And please report any issues you’re having with this upgrade to:

Assuming there are no significant issues, I’ll release 2.8 final within a week or two.

Happy spec’ing!

David

rspec-core 2.7.1 is released!

October 20th, 2011

rspec-core-2.7.1

full changelog

  • Bug fixes
    • tell autotest the correct place to find the rspec executable