Blog

Upgrading to RSpec 2 with Ruby on Rails 3

Jonathon Horsman Oct 15 20 comments

Following on from my previous post about upgrading our Rails 2.3.5 application to Rails 3 the most time consuming part was getting our 600 tests passing again.

We use Rspec (upgraded to version 2), Ruby Double (rr) for mocking and Factory Girl.

Here’s the process I followed to get our recently upgraded Rails 3 app working with Rspec 2.

There’s a special place in my heart for the seemingly deliberately terse docs which come with Rspec.

The first 2 lines of the home page says it all:

Overview
Behaviour Driven Development for Ruby.

and that’s it.

Perhaps they’re worried about running out of space on the internet?

Anyway I digress, getting my tests all working again was a bit of a mission.

Update the Gemfile

First the updated Gem versions. At the bottom of my Gemfile they look like this:

group :test do
  gem "rspec"
  gem "rspec-rails"
  gem "autotest"
  #gem "factory_girl", "2.0.0.beta1"
  gem "factory_girl_rails"
  gem "rr"
end

New Spec Helper file

The next step is to update to the new spec_helper.rb file:

ENV["RAILS_ENV"] ||= 'test'

require File.expand_path("../../config/environment", __FILE__)
require 'rspec/rails'
require 'thinking_sphinx/test'

# Requires supporting ruby files with custom matchers and macros, etc,
# in spec/support/ and its subdirectories.
Dir[Rails.root.join("spec/support/**/*.rb")].each {|f| require f}

RSpec.configure do |config|
  # == Mock Framework
  config.mock_with :rr

  # Remove this line if you're not using ActiveRecord or ActiveRecord fixtures
  config.fixture_path = "#{::Rails.root}/spec/fixtures"
  
  ThinkingSphinx::Test.init

  # If you're not using ActiveRecord, or you'd prefer not to run each of your
  # examples within a transaction, remove the following line or assign false
  # instead of true.
  config.use_transactional_fixtures = true
end

Broken backwards compatibility

For apparently no good reason there’s a bunch of things which just no longer work.

Sometimes it’s just because they’ve decided to rename the methods.

For instance in all my controller tests I had to do a global search for integrate_views and replace with render_views

Rails params bug

Previously all params in a test get/post/delete/put request would be converted to strings, just as they would appear to your controller in the real world.

However there’s a bug in Rails 3 which means this no longer happens. So where this used to pass:

describe "find contact" do
  before { mock(Contact).find("1234") { Contact.new } } # expect a string for the ID
  describe "successfully" do
    before { get :show { :id => 1234 } } # integer param passed. This used to be converted to a string for me
    it "should render the show view" do
      response.should render_template("show")
    end
  end
end

it now fails with:

unexpected method invocation:
find(1234)
expected invocations:
- find("1234")

I had about 300 of these tests failing and I thought I was going to have to modify every one of them.

Interestingly after digging through the Rails code I found the bug. Evidently this “params massaging” was not deliberately removed because the code is still there. It just overwrites the massaged parameters with the original parameters.

So here’s my fix. Create the file spec/support/action_controller.rb and copy in the content:

module ActionController
  class TestCase < ActiveSupport::TestCase
    module Behavior
      def process(action, parameters = nil, session = nil, flash = nil, http_method = 'GET')
        # Sanity check for required instance variables so we can give an
        # understandable error message.
        %w(@routes @controller @request @response).each do |iv_name|
          if !(instance_variable_names.include?(iv_name) || instance_variable_names.include?(iv_name.to_sym)) || instance_variable_get(iv_name).nil?
            raise "#{iv_name} is nil: make sure you set it in your test's setup method."
          end
        end
        
        @request.recycle!
        @response.recycle!
        @controller.response_body = nil
        @controller.formats = nil
        @controller.params = nil
        
        @html_document = nil
        @request.env['REQUEST_METHOD'] = http_method
        
        parameters ||= {}
        @request.assign_parameters(@routes, @controller.class.name.underscore.sub(/_controller$/, ''), action.to_s, parameters)
        
        @request.session = ActionController::TestSession.new(session) unless session.nil?
        @request.session["flash"] = @request.flash.update(flash || {})
        @request.session["flash"].sweep
        
        @controller.request = @request
        #@controller.params.merge!(parameters) # this is the offending line, which I removed
        build_request_uri(action, parameters)
        Base.class_eval { include Testing }
        @controller.process_with_new_base_test(@request, @response)
        @request.session.delete('flash') if @request.session['flash'].blank?
        @response
      end
    end
  end
end

now all those tests should start passing again.

RSpec matchers

A bunch of matchers seem to have been taken out now, but they’re easy enough to roll your own as demonstrated in this video

I wrote the include_text matcher so my old controller tests which look like this will now pass:

response.should include_text('Error: Your update failed')

Yes, this is a view test which doesn’t belong in the controller, but I’m not about to go through all my tests and remove a useful test.

So I created a new file called spec/support/include_text.rb and added this:

module RSpec::Rails
  module Matchers
    RSpec::Matchers.define :include_text do |text|
      match do |response_or_text|
        @content = response_or_text.respond_to?(:body) ? response_or_text.body : response_or_text
        @content.include?(text)
      end

      failure_message_for_should do |text|
        "expected '#{@content}' to contain '#{text}'"
      end

      failure_message_for_should_not do |text|
        "expected #{@content} to not contain '#{text}'"
      end
    end
  end
end

Factory Girl gem change

If you’re using Factory Girl, replace factory_girl in your Gemfile with factory_girl_rails

I don’t know the rationale behind the change but cest la vie.
edit: after looking at the code it appears Factory Girl Rails is just a small wrapper around Factory Girl, so you will still need the Factory Girl gem, probably at version 2.

Assert difference has disappeared

Another useful test method has been inexplicably removed. A complete list of everything which has been removed / renamed would be very useful. I’m looking at you, David Chelimsky

Anyways, to get my assert_difference and assert_no_difference assertions working again, I created the file spec/support/assert_difference.rb:

def assert_difference(executable, how_many = 1, &block)
  before = eval(executable)
  yield
  after = eval(executable)
  after.should == before + how_many
end

def assert_no_difference(executable, &block)
  before = eval(executable)
  yield
  after = eval(executable)
  after.should == before
end

Hey I’m getting closer, down to 72 failures.

Loading files in the lib directory

The ruby files in lib/ are no longer automatically loaded by Rails. So in application.rb I had to add this:

module Matchbook
  class Application < Rails::Application
    require Rails.root.join("lib", "matchbook", "delayed_jobs", "delete_contacts_job.rb")
    require Rails.root.join("lib", "matchbook", "delayed_jobs", "email_creator_job.rb")
    require Rails.root.join("lib", "matchbook", "auditing", "contact_audit_sweeper.rb")

    ...
  end
end

More Rspec pain

I also get a few of these errors:

     unexpected method invocation:
       valid?(nil)
     expected invocations:
     - valid?()

Come on, why aren’t they treated as equivalent now?
Well since there’s only a few of them I went and manually added the nil parameter.

Action Mailer changes

According to the docs ActionMailer methods always return objects, not strings:

Every object in a Mail object returns an object, never a string. So Mail.body returns a Mail::Body class object, need to call encoded or decoded to get the string you want.

So where these tests used to pass

    mail = SiteMailer.email(message, contact, {"message" => message_link, "image" => image_link, "promo" => promo_link})
    
    mail.body.should include_text(message_link.to_s)
    mail.body.should include_text(image_link.to_s)
    mail.body.should include_text(promo_link.to_s)

they now fail. That’s easily fixed with:

    mail = SiteMailer.email(message, contact, {"message" => message_link, "image" => image_link, "promo" => promo_link})

    body = mail.body.encoded.gsub(/=\r\n/, "")
    body.should include_text(message_link.to_s)
    body.should include_text(image_link.to_s)
    body.should include_text(promo_link.to_s)

Note I updated the SiteMailer code to work with the new Rails mailer API. Info in the guide.

Ruby Double mocking with deprecations

This no longer works:

any_instance_of(Note, :validate => nil)

and also produces deprecation warnings. This isn’t the correct rr syntax anyway, so I replaced it with

stub.any_instance_of(Note).valid?(nil) { true }

Display the full stacktrace

Run specs with the -b option to include the full backtrace, rather than just the subset which includes your code.

This is useful for identifying problems in libraries and dependencies.

All tests passing

With some further code changes to work with the new Mail API, fixes to rename a reserved word (field is a no-no column name now) and a couple of other bits and pieces, all 613 of my tests now pass!

Comments //

David Chelimsky

David Chelimsky Oct 20

I'll update the docs with things you've cited that are actually missing, but most of this information is actually readily available. Please read the following and update your post accordingly:

1. http://rspec.info - "More Information" (links to RSpec-2 documentation).

2. http://github.com/rspec/rspec-rails (README - linked from http://rspec.info).

3. http://github.com/rspec/rspec-rails/blob/master/Upgrade.markdown

Additional notes:

a. `assert_difference` is a Rails assertion. RSpec's `should change` is still available and at your disposal.

b. you're using RR for mocking. That's what is providing failure messages about invocations with or without nil.

Hope this helps you and your readers.

Cheers,
David

Kurt Snyder

Kurt Snyder Feb 01

There's a cheat sheet of RSpec1 to RSpec2 changes here: http://snyderscribbles.blogspot.com/2011/01/rspec-2-changes-from-rspec-1.html I'll post additions/corrections that anyone wants to submit.

Kimlueng

Kimlueng Sep 14

Thanks for the pointer. That'll help if I need to conrvet anything else. I'm impressed by how much Rspec has changed in just the last couple of months. I think the older release I was working from dates back only as far as the start of April.

chinacheng

chinacheng Feb 22

i think the assert_difference method could be more better, for example: executable maybe a array

Discount Codes UK

Discount Codes UK Oct 19

Hurrah! Finally I got a weblog from where I know how to truly obtain helpful facts concerning my study and knowledge.

ImminsRaisk

ImminsRaisk Nov 28

唆している。 一つには、親と子は同じ興味を共有することはでき <a href=http://www.africa-divine-safaris.com/%E3%82%B5%E3%83%B3%E3%82%B0%E3%83%A9%E3%82%B9-c-11.html>シャネル サングラス 人気</a> ットレタリングを使用したり、あなたの基本的な招待状に滑らかに <a href=http://www.alnebrasgames.com/%E3%83%9E%E3%83%95%E3%83%A9%E3%83%BC-c-35.html>マフラー</a> いキャリアの見通しを活用することができます。 入手可能な最新 <a href=http://www.bvlgariperfumejp.com/%E3%83%96%E3%83%AB%E3%82%AC%E3%83%AA%E9%A6%99%E6%B0%B4%E3%83%AC%E3%83%87%E3%82%A3%E3%83%BC%E3%82%B9-c-1.html>香水 おすすめ</a> う。

 単一の株式仲買人は、よくやった - 辛うじて <a href=http://www.djbrixx.com/category-10.html>ダウンジャケット モンクレール</a> めに静かな雰囲気を提供しています。

 占星術の歴史は文 <a href=http://www.gyikom.com/category-3.html>グッチの財布</a> デートは、あなたの途方もない空想を共有し、探求して自由であるです。 あなたは間違いなくあなたがcriminals.Bac

enetaTubinake

enetaTubinake Nov 28

く作成されたクジャンは地元の美術館で見ることができるように有 <a href=http://viewdesigncompany.com/news/moncler.php>モンクレール ワンピース</a>
 私たちはただ、時々、彼らは本当にpopular.Soい <a href=http://www.aboveboardadvisors.com/%E3%83%AD%E3%83%AC%E3%83%83%E3%82%AF%E3%82%B9-c-28.html>ロレックス ブログ</a> 1、ビジネス客のためにB2、学生ビザのようなビザの他のタイプ <a href=http://www.broerwenang.com/%E3%83%8D%E3%82%AF%E3%82%BF%E3%82%A4-c-19.html>ネクタイ 結び方</a> 特に、ほとんどの黒の女性は非常に性的である。 ステレオタイ <a href=http://www.dedicatedserver411.com/%E3%82%BB%E3%82%A4%E3%82%B3%E3%83%BC5-%E3%82%B9%E3%83%9D%E3%83%BC%E3%83%84-c-4.html>セイコー5 日本製</a> ます。

 試験が終了するまで、彼らは定期的な生活を続け <a href=http://www.latinabarbie.net/%E3%83%A2%E3%83%B3%E3%82%AF%E3%83%AC%E3%83%BC%E3%83%AB%E3%83%A1%E3%83%B3%E3%82%BA%E3%83%99%E3%82%B9%E3%83%88-c-6.html>モンクレール ダウン 2014 メンズ</a> ンテキストに理論を入れて学生に教室の範囲外で勉強する機会を与ョブを実行する能力と意欲を示してグリーンカードを得ることがで

ImminsRaisk

ImminsRaisk Nov 28

新しいモデル車は、売りにもだが、入札はsoon.Smallビ <a href=http://bibelos.com/>スープラ</a> うに彼の下の大理石の台座に扱われます。 彼のプロポーションは <a href=http://www.als-risk.com/%E3%83%97%E3%83%A9%E3%83%80-%E3%83%9D%E3%83%BC%E3%83%81-c-1.html>プラダ ポーチ 2013</a> の財布の内側に到達し、感電する危険にしたくないので、必ず安全 <a href=http://www.burberrywholesale.com/%E3%83%90%E3%83%BC%E3%83%90%E3%83%AA%E3%83%BC%E3%83%AD%E3%83%B3%E3%83%89%E3%83%B3--c-15.html>バーバリーロンドン ネクタイ</a> 親族を探しすべき? 家系図の名前、あなたは国家重要な記録の系 <a href=http://www.fgjxmk.com/>mcm 激安</a> ないことを何かでなければなりません 予算を決めることです。
<a href=http://www.stabnsplash.com/jp/category/l4_12.html>ダウン/ダウンジャケット</a> 特権。 カリフォルニアの逮捕情報の要求はサクラメント、カリフもしれないが後はそのはるかに良い人生になります。 あなたのポ

UGGS On Sale

UGGS On Sale Dec 03

Because the admin of this web site is working, no hesitation very soon it will be famous, due to its feature contents.

vigrx oil price

vigrx oil price Mar 19

Very good post! We are linking to this particularly great article
on our site. Keep up the great writing.

コピーブランド

コピーブランド Apr 14

buranndofukuはガガミラノ コピー:http://www.buranndofuku.com/gallery-163-grid.html
 日本でも広く流通してしまっているモンクレール 偽物通販店です。
 商品の見分け方や 注意点について紹介しています。
 クロムハーツ 激安の商品特に大人気のクロムハーツ激安 ,クロムハーツ ブランド偽物 格安の種類を豊富に取り揃えます。スーパーコピーブランド:http://www.buranndofuku.com/のお求めはぜひ当店へ!
[url=http://www.buranndofuku.com/gallery-181-grid.html]コピーブランド[/url]

money Network bank of america

money Network bank of america Apr 30

I think this is among the most vital information for me. And i
am glad reading your article. But wanna remark on
few general things, The website style is perfect, the articles
is really great : D. Good job, cheers

http://marseille-tonight.com

http://marseille-tonight.com May 03

Wonderful beat ! I wish to apprentice even as you amend your website, how can i subscribe for
a blog web site? The account aided me a applicable deal.
I were a little bit familiar of this your broadcast offered brilliant clear idea

copy paste cash system scam

copy paste cash system scam May 15

It's genuinely very complex in this busy life to listen news on TV, thus I just use web
for that reason, and obtain the hottest news.

Deandre

Deandre Jun 17

I have read so many content concerning the blogger lovers however this post is truly a pleasant piece of writing, keep it up.

success coach

success coach Jun 24

Marvelous, what a web site it is! This website provides valuable information to us, keep it up.

Angelita

Angelita Jun 25

Hi! I could have sworn I've visited this site before but after going through a
few of the articles I realized it's new to me.
Anyways, I'm definitely pleased I found it and I'll be bookmarking it and checking
back regularly!

delray beach summer youth programs

delray beach summer youth programs Jul 01

The arcade games, adventure and fantasy games can really broaden the children's
minds, show them new worlds, and even help them develop their fantasies.
It should take care of your expenditures on food and lodging for the whole camp.
" Make sure you have a phone or portable radio that you can use to contact someone for help. First thing that you should remember is whether your child should attend a day camp or residential camp. Patrick's Day, Camp Bow Wow has announced that several participating camps will celebrate "Lucky Dogs Go To Camp" week March 12-17. You do not need to worry about your child as he is in safe hands. Where is the camp located, and what are the facilities like. It may have had its beginning in rural America, but these days, 4-H offers activities as diverse as rock climbing, ecology, cooking, forestry, citizenship, animal care and more. So when they put their heart and soul in this course they will not only learn about the particular subject but they will also attain mental growth, which helps them in the long run. We have the gear you need for cross country skiing.

little black bodycon dress

little black bodycon dress Jul 18

Besides of being the field of fashion, you may also
prepare the costly expenses of the profession you may want to take on.
Resources may grace with your presence to related
topics, such as grooming, fashion, and etiquette and not just fashion modeling
alone. Being a model requires more than just being a pretty face.

http://yuki.alinggo.net/

http://yuki.alinggo.net/ Jul 19

Hello there, just became alert to your blog through Google, and
found that it is really informative. I'm going to watch out for
brussels. I'll appreciate if you continue this in future.

Numerous people will be benefited from your writing.

Cheers!

Post a comment

  1. optional

Recent Tweets

Blog: The monumental Myspace cock-up: http://bit.ly/emgRKV
Tweeted on Friday at 09:43

Awww railsapi, delete some logs: http://bit.ly/htBNDH
Tweeted on Wednesday at 16:15