Pivotal Labs

Main menu

Skip to primary content
Skip to secondary content
  • About
  • Case Studies
  • Team
    • Executives
    • Locations
      • San Francisco (HQ)
      • Boston
      • Boulder
      • Denver
      • London
      • Los Angeles
      • New York
  • Community
    • Blogs
    • Tech Talks
    • Events
  • Careers
    • Lifestyle
    • Principles & Practices
    • Benefits
    • FAQ
    • Apply
  • Contact
    • Press Room
    • Press Releases
    • In The News
    • Press Kit
  • All
  • Labs
  • Standup
  • Tracker

BetterReceive: A More Assertive Mock

Steve Ellis
Friday, February 8, 2013

One thing I’ve always liked about TDD is the ease with which it guides you through development. Given an outside-in approach, one failing test will lead you into another, and as you develop you dive deeper. Each time you make a new set of tests pass you step back to the earlier context and continue until you’ve driven out your story one test at a time. This all works seamlessly, with the exception of adding new methods to a class. For example:

class Developer; end

class Office
  attr_accessor :developers
end

Now, we want to tell the office how to run. Testing it with RSpec would look something like this:

describe Office do
  describe "#run" do
    it "gives the developers a break to play ping pong" do
      dev = Developer.new
      office = Office.new(developers: [dev])

      dev.should_receive :break_for_ping_pong
  
      office.run
    end
  end
end

This test fails because dev does not receive break_for_ping_pong. From here making our tests pass is easier than actually making our code work:

class Office
  attr_accessor :developers

  def run
    developers.each(&:break_for_ping_pong)
  end
end

We run the tests, and we’re done! Everything passes. Time for a ping pong break. Oh, wait… dev still doesn’t respond to break_for_ping_pong, but the tests pass because dev does receive break_for_ping_pong.

Due to the dynamic nature of Ruby and all its meta-programming goodness/complexity, RSpec does not try to predict whether or not objects respond to the methods that are mocked out. Sometimes this is for the best, but in cases like the one above it would be nice to recognize the missing behavior.

Enter BetterReceive. BetterReceive works like any other RSpec mock, with the exception that before mocking/stubbing a method there is an extra assertion that the object under test responds to the specified method.

So, after including ‘better_receive’ in the Gemfile, we can update our test to be more assertive:

      dev.better_receive :break_for_ping_pong

Now we have a properly failing test, so we put off the ping pong break and make the code pass:

class Developer
  def break_for_ping_pong
    # ...
  end
end

More important than driving out new functionality, now that we have working code BetterReceive will catch bugs later on. Imagine break_for_ping_pong was called in other places in the code.

class Pair
  attr_accessor :developers

  def disagree
    developers.each(&:break_for_ping_pong)
  end
end

Changing the implementation of Pair may lead to a refactoring in the Developer model, for example moving break_for_ping_pong onto Pair. The other places in the code base that use break_for_ping_pong may go unnoticed due to oversight, lack of discoverability, or however your bugs slip in. Had we left should_receive in our test for Office the suite would continue improperly passing, harboring a new bug. Since dev still receives break_for_ping_pong, the should_receive assertion passes even though dev doesn’t respond to break_for_ping_pong. BetterReceive catches these regressions and alerts you before letting new changes get too far.

 
#responds_to? Considered Helpful

Ruby’s method_missing feature is the main thing that stood in the way of tools like RSpec from being able to consistently predict whether an object responds to a method. For this reason, Ruby 1.9.2 introduced responds_to_missing?. The folks over at Thoughtbot have a great post about why you should always define respond_to_missing? when overriding method_missing.

Without updating what an object responds to you are pitting two of Ruby’s biggest strengths against each other: Meta-Programming vs. Duck Typing. (Spoiler: Duck typing loses.) This tradeoff doesn’t have to be made. Many libraries do a good job of updating responds_to? when changing method_missing, but they don’t all. BetterReceive can help you determine which parts of your own code and which libraries you use that have not yet updated responds_to_missing?. Those libraries better receive some pull requests.

Ideally, I would like to see the responds_to? assertion become the default when mocking/stubbing. While there are other obstacles to overcome in making this possible, consistently updating responds_to? is the most important.

  • 0 Shares
  • Share on Facebook
  • Share on Twitter

Tuesday Brown Bag: @ykatz

Steve Ellis
Monday, June 18, 2012

Events

  • Tuesday Brown Bag: Yehuda

Yehuda Katz will be giving a brown bag on Tuesday at 12:30.

  • 0 Shares
  • Share on Facebook
  • Share on Twitter

NYC Standup: 2/5-10 Batched

Steve Ellis
Friday, February 10, 2012

Interesting

  • assert_template Matching: RSpec’s template matcher uses a reg ex, which is loose enough to cause some false positives.
  render "_foo_bar.haml"

will pass cause both of these to pass:

  should render_template "_foo_bar.haml"
  should render_template "_bar.haml"

The best solution presented was to explicitly say what you expect not to be rendered(should_not render_template “_bar.haml”). There has been some activity on Github recently around ActionController’s assert_template method, but none addressing this exact problem(1 example: https://github.com/rails/rails/pull/3879/files). The regex is complicated by the fact that it has to match ambiguous partials rendered with specific paths(should render_template “_bar.haml” needs to match render “foo/_bar.haml”).

  • Let It Be: If you declare a “let!” variable at the top of a file, and redeclare that variable with a “let” statement in a nested context, the variable will not be lazily loaded. RSpec evaluates it as a “let!”.

  • Binary Tempfiles: There is no way to open a new Tempfile in binary mode; you must open it first and then call foo.binmode.

  • Airbrake was acquired by Exceptional
    http://techcrunch.com/2012/02/07/exceptional-acquires-error-tracking-application-airbrake/

  • Auto compiling SASS, pre-3.0: Adam used ‘compass watch’ and Foreman to automate the regeneration of stylesheets in development. This prevents him from having to wait for the sass to recompile on the first request he makes after modifying stylesheets. https://github.com/ddollar/foreman

  • Should receive, in any order: Ian & Suman were attempting to write an RSpec 1.3 spec that asserted a method was called with an array that included the correct elements; however, the order of the elements within the array was not important. Seems there is no array_including equivalent to hash_including. The solution they came to was to use the block style expectation as defined here: https://www.relishapp.com/rspec/rspec-mocks/docs/argument-matchers

  foo.should_receive(:bar) do |arg1|
    arg1.should =~ [3,1,2]
  end

Anyone know of a built-in matcher for arrays similar to hash_including?

  • 0 Shares
  • Share on Facebook
  • Share on Twitter

NYC Standup 08/12/11: Stretches!

Steve Ellis
Friday, August 12, 2011

Interesting

  • Artifice test pollution: The Lees ran into an issue where every fifth selenium test was failing. They tracked it down to using Artifice to stub responses in their tests. Since Webdriver communicates with the browser through http requests the stubbed Artifice responses were being received by Webdriver, causing unexpected behavior. Be careful stubbing with Artifice or you may accidentally stub all localhost requests.

  • Devise stretches: Todd found that you can speed up your test suite by turning down Devise’s level of password encryption in your testing environment. He got a 10% bump by including the following code in spec_helper:

Devise.setup do |config|
   config.stretches = 1
 end

For more tips on cutting down your test suite’s run time:
http://www.rubyinside.com/careful-cutting-to-get-faster-rspec-runs-with-rails-5207.html

  • 0 Shares
  • Share on Facebook
  • Share on Twitter

NYC Standup 08/11/2011: Ruby Malloc Errors

Steve Ellis
Thursday, August 11, 2011

Interesting

  • Malloc/Seg Faults in 1.9.2: A few developers have been running into Malloc/Seg Fault errors recently. The cause is yet to be determined.
  • 0 Shares
  • Share on Facebook
  • Share on Twitter

NYC Standup 08/10/2011: Cycles

Steve Ellis
Wednesday, August 10, 2011

Interesting

  • cycle(even, odd): Jonathan pointed out a method in rails that allows you to apply classes to the even and odd numbered indexes of collections, allowing you to easily apply zebra striping patterns to a collection. Peter mentioned the alternative CSS pseudo-selector “nth-child,” which does the same thing with CSS and properly recalculates in the case where you remove an element with AJAX. Unfortunately, nth-child does not work with IE so if you have to support IE, use cycles, otherwise use nth-child.

  • will_paginate 3.0: Ian mentioned that version 3 of the will_paginate gem was recently released. It now uses scopes, bringing it up to speed with Kaminari, another popular pagination gem.

  • home_run hates ActiveSupport: JT and Micah pointed out that if you’re adding a duration to a DateTime using home_run, like “DateTime.now + 1.day,” it doesn’t play well with ActiveSupport. It assumes that the number on the right is supposed to be days, which it converts to seconds 86,400 and then changes to days again(86,400 days). The ‘+’ operator has been fixed in edge rails, but not yet back ported. They pointed out that although it doesn’t read well, using #since on any instance of DateTime will act how you would hope the ‘+’ operator would.

  • 0 Shares
  • Share on Facebook
  • Share on Twitter

NYC Standup 08/09/2011: EC2 Again?

Steve Ellis
Tuesday, August 9, 2011

Interesting

  • Sauce Labs requires Heroku: JT reported a problem he noticed where Sauce Labs tries to hit a Heroku instance. If you don’t have an instance set up on Heroku, the sauce gem hangs.

  • EC2 Downtime: Amazon EC2 experienced some downtime last night.

Events

NYC.rb presentation Tuesday at 7. The talk is titled, “Why doing things wrong is the right thing to do.” and will be given by Jon Distad as a preview of the talk he’ll give at the Ruby Hoedown.

  • 0 Shares
  • Share on Facebook
  • Share on Twitter

Standup 08/08/2011: Not from Canada

Steve Ellis
Monday, August 8, 2011

New Faces

We welcome Micah, a new Pivotal developer, to New York this week. Micah hails from Nevada City, CA.

Events

NYC.rb presentation Tuesday at 7. The talk is titled, “Why doing things wrong is the right thing to do.” and will be hosted by Jon Distad.

  • 0 Shares
  • Share on Facebook
  • Share on Twitter
Steve Ellis

Steve Ellis
New York

Subscribe to Steve's Feed

Author Topics

mocks (1)
rspec (1)
stubs (1)
testing (1)
agile (6)
  • About
  • Case Studies
  • Team
  • Community
  • Careers
  • Contact
  • Labs
  • Events

Contact Us

contact@pivotallabs.com
+1 415-77-PIVOT
TwitterLinkedInFacebook

Pivotal Tracker

Tracker is the award-winning agile project management tool that enables real-time collaboration around a shared, prioritized backlog.
Visit pivotaltracker.com >