David Chelimsky

random thoughtlessness

Myron Marston and Andy Lindeman are RSpec’s new project leads

| Comments

TL;DR

Myron Marston is taking over leadership of the RSpec project, and will be the lead maintainer of the rspec-core, rspec-expectations, and rspec-mocks gems.

Andy Lindeman is taking over as lead maintainer of the rspec-rails gem.

Myron Marston is RSpec’s new project lead

Myron Marston has been contributing to RSpec since the ramp up to the 2.0 release in 2010, and joined the core team in early 2011. In addition to solid contributions to the code base, Myron has taken responsibility for many bug reports, feature requests, pull requests, etc. He also makes a habit of answering RSpec-related questions on Stack Overflow and Twitter, and he does this all with thoughtfulness, patience, and wisdom.

I can’t think of a better choice to lead the RSpec project, so I invited him to do so. Thankfully, SEOmoz, Myron’s employer, is allowing him to work on RSpec during work hours, and so he accepted.

In addition to the overall project lead, Myron will be the lead maintainer of the rspec-core, rspec-expectations, and rspec-mocks gems. While Myron uses these core rspec libs every day for his work, he doesn’t do much with Rails, so we discussed and agreed that we’d ask somebody else to take care of the rspec-rails gem. Enter Andy Lindeman.

Andy Lindeman is the new lead maintainer of rspec-rails

Andy Lindeman is the newest addition to the rspec core team. Like Myron, Andy takes great care in shepherding pull requests and answering questions in addition to making his own rock solid contributions. Andy also writes Rails apps and does Rails training at Big Nerd Ranch, which puts him in a great position to ensure that rspec-rails keeps up with changes in Rails remains a great choice for developing Rails apps.

Thanks in part to Big Nerd Ranch for their support of Andy’s work on RSpec, Andy has agreed to take the lead on rspec-rails.

As for me …

When Dave Astels introduced Steven Baker’s new RSpec library to me back in 2005, I started submitting patches, Steven gave me commit rights and, a year later, he decided to move on to other work and offered me leadership of the project. I was overjoyed to accept.

In those days, I was teaching TDD/Refactoring courses for Object Mentor. I was encouraged to work on OSS in my bench time, and I was particularly interested in tools that helped to promote the documentation and design aspects of TDD. RSpec and some of the early definitions/discussions of BDD (“it’s all behavior!”) fit perfectly into my thinking and my world.

After I left Object Mentor, RSpec was part of my day to day work on Ruby applications, but most of my work on its maintenance moved to my spare time. This was fine at first, as I had the support of employers and family, but I found myself doing less and less of pretty much everything else that I enjoy and learn from.

Back in June, I joined a project team at DRW Trading that deals in a lot of Clojure and almost no Ruby. In the roughly 6 months since, I’ve been learning a new domain, a new language, new programming models, and even a new text editor. I consider myself extraordinarily fortunate to be able to learn all of these new things, but I’ve found myself less and less able to balance my work on RSpec with my job and with everything else I want to do.

And so, it is time.

Of course, I’ll continue to contribute to the project and surrounding conversation, and I look forward to seeing all my friends in the community at assorted conferences in the coming year(s). I’ll just be arriving without my RSpec Lead hat on.

Thank you to Steven Baker for handing me the wheel, and thank you to everybody who has participated in the project and all RSpec users for your support over these 6+ years. It’s been a great honor and a great pleasure.

rspec-2.12 is released

| Comments

rspec-2.12 is a minor release (per SemVer), which includes numerous enhancements and bug fixes. It is fully backward compatible with previous rspec-2 releases and is a recommended upgrade for all users.

Thanks to all who contributed. Special thanks to Myron Marston and Andy Lindeman for their personal contributions to the code as well as a great job shepherding pull requests from several new contributors.

UPDATE: If you’re an rspec/rails/capybara user, be sure to read Andy Lindeman’s blog post on Capybara-2.0 and rspec-rails.

Docs

RDoc

Cucumber features

Release notes

rspec-core-2.12.0

full changelog

Enhancements

  • Add support for custom ordering strategies for groups and examples. (Myron Marston)
  • JSON Formatter (Alex Chaffee)
  • Refactor rake task internals (Sam Phippen)
  • Refactor HtmlFormatter (Pete Hodgson)
  • Autotest supports a path to Ruby that contains spaces (dsisnero)
  • Provide a helpful warning when a shared example group is redefined. (Mark Burns).
  • --default_path can be specified as --default-path. --line_number can be specified as --line-number. Hyphens are more idiomatic command line argument separators (Sam Phippen).
  • A more useful error message is shown when an invalid command line option is used (Jordi Polo).
  • Add format_docstrings { |str| } config option. It can be used to apply formatting rules to example group and example docstrings. (Alex Tan)
  • Add support for an .rspec-local options file. This is intended to allow individual developers to set options in a git-ignored file that override the common project options in .rspec. (Sam Phippen)
  • Support for mocha 0.13.0. (Andy Lindeman)

Bug fixes

  • Remove override of ExampleGroup#ancestors. This is a core ruby method that RSpec shouldn’t override. Instead, define ExampleGroup#parent_groups. (Myron Marston)
  • Limit monkey patching of shared example/context declaration methods (shared_examples_for, etc.) to just the objects that need it rather than every object in the system (Myron Marston).
  • Fix Metadata#fetch to support computed values (Sam Goldman).
  • Named subject can now be referred to from within subject block in a nested group (tomykaira).
  • Fix fail_fast so that it properly exits when an error occurs in a before(:all) hook (Bradley Schaefer).
  • Make the order spec files are loaded consistent, regardless of the order of the files returned by the OS or the order passed at the command line (Jo Liss and Sam Phippen).
  • Ensure instance variables from before(:all) are always exposed from after(:all), even if an error occurs in before(:all) (Sam Phippen).
  • rspec --init no longer generates an incorrect warning about --configure being deprecated (Sam Phippen).
  • Fix pluralization of 1 seconds (Odin Dutton)
  • Fix ANSICON url (Jarmo Pertman)
  • Use dup of Time so reporting isn’t clobbered by examples that modify Time without properly restoring it. (David Chelimsky)

Deprecations

  • share_as is no longer needed. shared_context and/or RSpec::SharedContext provide better mechanisms (Sam Phippen).
  • Deprecate RSpec.configuration with a block (use RSpec.configure).

rspec-expectations-2.12.0

full changelog

Enhancements

  • Colorize diffs if the --color option is configured. (Alex Coplan)
  • Include backtraces in unexpected errors handled by raise_error matcher (Myron Marston)
  • Print a warning when users accidentally pass a non-string argument as an expectation message (Sam Phippen)
  • =~ and match_array matchers output a more useful error message when the actual value is not an array (or an object that responds to #to_ary) (Sam Phippen)

Bug fixes

  • Fix include matcher so that expect({}).to include(:a => nil) fails as it should (Sam Phippen).
  • Fix be_an_instance_of matcher so that Class#to_s is used in the description rather than Class#inspect, since some classes (like ActiveRecord::Base) define a long, verbose #inspect. (Tom Stuart)

rspec-mocks-2.12.0

full changelog

Enhancements

  • and_raise can accept an exception class and message, more closely matching Kernel#raise (e.g., foo.stub(:bar).and_raise(RuntimeError, "message")) (Bas Vodde)
  • Add and_call_original, which will delegate the message to the original method (Myron Marston).

Deprecations:

  • Add deprecation warning when using and_return with should_not_receive (Neha Kumari)

rspec-rails-2.12.0

full changelog

Enhancements

  • Support validation contexts when using #errors_on (Woody Peterson)
  • Include RequestExampleGroup in groups in spec/api

Bug fixes

  • Add should and should_not to CollectionProxy (Rails 3.1+) and AssociationProxy (Rails 3.0). (Myron Marston)
  • controller.controller_path is set correctly for view specs in Rails 3.1+. (Andy Lindeman)
  • Generated specs support module namespacing (e.g., in a Rails engine). (Andy Lindeman)
  • render properly infers the view to be rendered in Rails 3.0 and 3.1 (John Firebaugh)
  • AutoTest mappings involving config/ work correctly (Brent J. Nordquist)
  • Failures message for be_new_record are more useful (Andy Lindeman)

Agile Testing and BDD eXchange 2012 in NYC

| Comments

I’m excited to be presenting on Behavior-Driven Objects at the Agile Testing & BDD eXchange in NYC on Monday, October 1st (details below). From the conference website:

One of the original ideas behind BDD was that testing should be about behavior at all levels. Effectively, all automated tests can be viewed as “functional tests” of entry points, be they user facing or internal, crossing procedural boundaries or not. In this talk we’ll explore approaches to focusing on the behavior of objects, using language that also serves to document the expected behavior in ways useful to both technical and non-technical stakeholders.

The Agile Testing & BDD eXchange is a one-day conference with talks, open-space discussions and focus on ‘quality time’ between presenters and attendees. There is a nice blend of technical talks like Uncle Bob Martin talking about how to apply SOLID principles to test design, and more process oriented talks like Ellen Gottesdiener’s talk on product definition through structured conversation.

You can read the full program and purchase tickets at http://bit.ly/S0Ypb8 (be sure to use the promo-code “bddxnyc-community-discount”).

What: Agile Testing & BDD eXchange NYC

When: October 1st 2012

Where: The Ace Hotel NYC

Twitter: #BDDXNYC

rspec-2.11 is released!

| Comments

rspec-2.11.0 is out and filled with a bunch of new features. Big thanks to all who contributed, especially Justin Ko, Andy Lindeman (the newest addition to the RSpec core team) and Myron Marston for their great job addressing issues and shepherding pull requests.

Thanks also to Myron for all his work on two great new features: the new expectation syntax and support for stubbing constants.

rspec-core-2.11.0

full changelog

Enhancements

  • Support multiple --example options. (Daniel Doubrovkine @dblock)
  • Named subject e.g. subject(:article) { Article.new }
  • config.mock_with and config.expect_with yield custom config object to a block if given
    • aids decoupling from rspec-core’s configuation
  • include_context and include_examples support a block, which gets eval’d in the current context (vs the nested context generated by it_behaves_like).
  • Add config.order = 'random' to the spec_helper.rb generated by rspec --init.
  • Delay the loading of DRb (Myron Marston).
  • Limit monkey patching of describe onto just the objects that need it rather than every object in the system (Myron Marston).

Bug fixes

  • Support alternative path separators. For example, on Windows, you can now do this: rspec spec\subdir. (Jarmo Pertman @jarmo)
  • When an example raises an error and an after or around hook does as well, print out the hook error. Previously, the error was silenced and the user got no feedback about what happened. (Myron Marston)
  • --require and -I are merged among different configuration sources (Andy Lindeman)
  • Delegate to mocha methods instead of aliasing them in mocha adapter.

rspec-expectations-2.11.0

full changelog

Enhancements

  • Expand expect syntax so that it supports expections on bare values in addition to blocks (Myron Marston).
  • Add configuration options to control available expectation syntaxes (Myron Marston):
    • RSpec.configuration.expect_with(:rspec) { |c| c.syntax = :expect }
    • RSpec.configuration.expect_with(:rspec) { |c| c.syntax = :should }
    • RSpec.configuration.expect_with(:rspec) { |c| c.syntax = [:should, :expect] }
    • RSpec.configuration.add_should_and_should_not_to Delegator

Bug fixes

  • Allow only Numeric values to be the “actual” in the be_within matcher. This prevents confusing error messages. (Su Zhang @zhangsu)
  • Define should and should_not on BasicObject rather than Kernel on 1.9. This makes should and should_not work properly with BasicObject-subclassed proxy objects like Delegator. (Myron Marston)

rspec-mocks-2.11.0

full changelog

Enhancements

  • expose ArgumentListMatcher as a formal API
    • supports use by 3rd party mock frameworks like Surrogate
  • Add stub_const API to stub constants for the duration of an example (Myron Marston).

Bug fixes

  • Fix regression of edge case behavior. double.should_receive(:foo) { a } was causing a NoMethodError when double.stub(:foo).and_return(a, b) had been setup before (Myron Marston).
  • Infinite loop generated by using any_instance and dup. (Sidu Ponnappa @kaiwren)
  • double.should_receive(:foo).at_least(:once).and_return(a) always returns a even if :foo is already stubbed.
  • Prevent infinite loop when interpolating a null double into a string as an integer ("%i" % double.as_null_object). (Myron Marston)
  • Fix should_receive so that null object behavior (e.g. returning self) is preserved if no implementation is given (Myron Marston).
  • Fix and_raise so that it raises RuntimeError rather than Exception by default, just like ruby does. (Andrew Marshall)

rspec-rails-2.11.0

full changelog

Enhancements

  • The generated spec/spec_helper.rb sets config.order = "random" so that specs run in random order by default.
  • rename render_template to have_rendered (and alias to render_template for backward compatibility)

Bug fixes

  • “uninitialized constant” errors are avoided when using using gems like rspec-rails-uncommitted that define Rspec::Rails before rspec-rails loads (Andy Lindeman)

Spec smell: explicit use of subject

| Comments

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:

article_spec.rb
1
2
3
describe Article do
  it { should validate_presence_of(:title) }
end

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

article_spec.rb
1
2
3
4
5
6
describe Article do
  it "validates presence of :title" do
    article = Article.new
    article.should validate_presence_of(:title)
  end
end

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:

article_spec.rb
1
2
3
4
describe Article do
  subject { Article.new }
  # ...
end

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:

article_spec.rb
1
2
3
4
5
6
describe Article do
  # ...
  it "validates presence of :title" do
    subject.should validate_presence_of(:title)
  end
end

Here is what they look like together:

article_spec.rb
1
2
3
4
5
6
7
8
# not recommended
describe Article do
  subject { Article.new }

  it "validates presence of :title" do
    subject.should validate_presence_of(:title)
  end
end

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:

article_spec.rb
1
2
3
4
5
6
7
describe Article do
  def article; Article.new; end

  it "validates presence of :title" do
    article.should validate_presence_of(:title)
  end
end

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:

article_spec.rb
1
2
3
4
5
6
# refactoring toward implicit step 1
describe Article do
  it "validates presence of :title" do
    subject.should validate_presence_of(:title)
  end
end

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:

article_spec.rb
1
2
3
4
5
6
# refactoring toward implicit step 2
describe Article do
  it "validates presence of :title" do
    should validate_presence_of(:title)
  end
end

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

article_spec.rb
1
2
3
describe Article do
  it { should validate_presence_of(:title) }
end

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:

american_citizen_spec.rb
1
2
3
4
5
6
7
8
describe AmericanCitizen do
  context "18 years of age" do
    subject { Person.new(:birthdate => 18.years.ago) }
    it { should     be_able_to(:vote)   }
    it { should     be_able_to(:enlist) }
    it { should_not be_able_to(:drink)  } # srsly?
  end
end

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:

checking_account_spec.rb
1
2
3
4
5
6
7
describe CheckingAccount, "with a non-zero starting balance" do
  subject(:account) { CheckingAccount.new(Money.new(50, :USD)) }
  it { should_not be_overdrawn }
  it "has a balance equal to the starting balance" do
    account.balance.should eq(Money.new(50, :USD))
  end
end

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

rspec-mocks and rspec-rails-2.10.1 are released!

| Comments

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!

| Comments

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)

rspec-expectations-2.9.1 is released!

| Comments

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!

| Comments

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.

Validations are behavior, associations are structure

| Comments

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:

article.rb
1
2
3
4
class Article < ActiveRecord::Base
  validates_presence_of :title
  has_many :comments
end

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:

validate_presence_of_title.rb
1
2
3
describe Article do
  it { should validate_presence_of(:title) }
end

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.

with_comments_by.rb
1
2
3
4
5
6
7
8
9
describe Article do
  describe "#with_comments_by" do
    it "finds articles with comments by the submitted comment_author" do
      article = Factory(:article)
      article.comments << Factory.build(:comment, :author => "jdoe")
      Article.with_comments_by("jdoe").should eq([article])
    end
  end
end

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:

rental_contract.rb
1
2
3
4
5
6
7
8
9
10
class RentalContract < ActiveRecord::Base
  has_many :monthly_charges
  has_one :rentable, :polymorphic => true

  validates_presence_of :rentable

  def default_monthly_charge
    price / months_applied
  end
end

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.

contract.rb
1
2
3
4
5
class Contract < ActiveRecord::Base
  validates_presence_of :name
  has_many :monthly_expenses
  calculates_default_monthly_charge
end

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.