Spec smell: explicit use of subject
May 13th, 2012
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!


May 15th, 2012 at 1:25 pm
And a pattern that I’ve seen over and over that makes tests even less maintainable is the use of #its with a really contrived subject. For example:
subject { people.to_hash[0] }
a few lines down
its([:age]) { should == 18 } its([:gender]) { should == ‘M’ }
etc.
Please, let’s stop this
May 15th, 2012 at 3:20 pm
Harold - agree entirely. We’re actually planning to extract #its to a separate gem when we release rspec-3. There are already a couple of subject/its gems out there that we may recommend if they cover all the bases #its does now. See https://github.com/dnagir/its, https://github.com/ZenCocoon/rspec-subject-extensions
May 15th, 2012 at 3:59 pm
This really reduces the convenience of shared specs, if we can’t rely on
subjectreferring to the unit under test.I’d like to see a compromise that let us name the thing under test while also letting RSpec know that it IS the thing under test. How would you feel about something like this:
https://gist.github.com/2705101
May 15th, 2012 at 4:22 pm
Or why not just make ’subject’ be a special kind of ‘let’?
https://gist.github.com/2705200
Even there, I’m not sure that exposing ’subject’ is worth it. Hm.
May 15th, 2012 at 4:28 pm
Avdi, Bradley - check out https://github.com/rspec/rspec-core/issues/619
I was actually working on that at home already as I wrote this article, and decided NOT to do it, but now that you guys thought of it, I’ll go ahead and add it.
June 5th, 2012 at 4:24 pm
Nice write, although I don’t find implicit assertions are easier to understand than using subject explicitly. sorry.
June 29th, 2012 at 9:27 pm
While reading the article I was about to suggest pretty much the same thing and then I got to the update at the bottom of the article and instantly became very happy.
Fantastic change.
August 3rd, 2012 at 9:40 am
Why not use the
let(:something) { Something.new }
basically completes the same thing.
September 19th, 2012 at 4:15 am
Hm, that’s pretty informative! Nice to learn it!
January 1st, 2013 at 8:52 am
Your update to this has really confused me Dave. I came from the deck of cards example (http://benscheirman.com/2011/05/dry-up-your-rspec-files-with-subject-let-blocks) then saw the link to this. I am currently using Michael Hardl’s tutorial.
So first it is good, then you say it’s bad, then you say it might be good and have updated it, but then Michael uses it in his tutorial (Subject { page } ) for testing a page.
I am really, really confused about why it’s good or bad. I understand there are opinions, but for a relative beginner (I am on chapater 9 of the rails tutorial) all the conflicting advice leaves me perplexed.
I also have your book (”The Rspec Book”) and the testing code in this seems different again from what I see out in the wild (i.e. above site and Rails Tutorial).
In fact, I would say that the most difficult and time consuming part of the last 6 months of learning Rails has been about 20% to do with Rails and 80% wondering what the testing code is doing and what the syntax means, seeing as everyone seems to use it differently.
I am not criticising you - I just think we need some kind of clarity, otherwise beginners like me are going to be wasting loads of time vacillating over how to test rather than coding.
January 1st, 2013 at 9:11 am
That all sounded a bit grumpy - I don’t mean it to, it’s more a heartfelt plea
I do appreciate rspec greatly. It’s just it is the source of a lot of confusion because of the (seemingly) diametrically oppsed views I keep reading (and that’s after I simply stopped reading DHH’s comments - guy needs some diplomacy lessons
February 13th, 2013 at 5:59 pm
It looks like most of your code samples are missing for some reason in this post.
March 13th, 2013 at 9:18 pm
Ric - I saw that and then refreshed and everything showed up. How’s that for confidence-building? I’ll look into what that’s happening.
May 10th, 2013 at 8:29 am
David. How does one do acceptance testing?
e.g.:
describe ‘Voting’ do let(:person) { Factory.attributes_for :person }
before do visit ‘/addperson’ register person submit_vote_for person end
it ‘increases vote count by one’ do find(’#vote_count’). text.should eq ‘1′ end end
May 10th, 2013 at 8:32 am
Check this gist. https://gist.github.com/snuggs/5554348
May 13th, 2013 at 3:52 am
Woah this specific site is fantastic i enjoy studying your posts. Keep up the excellent do the job! You understand, many persons are looking round due to this info, you are able to enable them to drastically.