Testing Sinatra application methods in isolation with RSpec

Sometimes you may want to test some Sinatra app methods in isolation (outside of a standard request flow). You probably even tried to do something like this:

require 'spec_helper'

describe App do
  subject { App.new }

  it 'expect run some specs here'
    expect(subject.current_user).to eq user
  end
end

Unfortunately you will end up with error like this:

NoMethodError: undefined method `current_user' for #<App app_file="/home/something/app.rb">
from (irb):2
from /home/.rvm/rubies/ruby-2.1.2/bin/irb:11:in `<main>'

This will occur, because Sinatra's App new method creates a Sinatra::Wrapper, not an App instance. Of course everything will work as it should, if you follow the get/post/put/delete way of testing Sinatra apps:

require 'spec_helper'

describe App do
  before { get '/api/v1/credit_cards.json' }
    
  it { expect(last_response).to be_ok }
end

but it will fail when trying to invoke any App methods directly.

Luckily there's a really simple solution to this issue. You just need to initialize it by manually allocating space and "bypassing" the new method:

require 'spec_helper'

describe App do
  subject do
    app = described_class.allocate
    app.send :initialize
    app
  end

  describe '#current_user' do
    before do
      expect(subject)
        .to receive(:params)
        .and_return(params)
        .at_least(:once)
    end

    context 'when there is no user_id' do
      let(:params) { {} }

      it { expect(subject.send :current_user).to be_nil }
    end
  end
end

That way you can get the "real" app instance (not a wrapper) that you can use as a subject in your specs. That way you can test your Sinatra app methods in isolation - without having to call a full request.

Categories: Ruby, Software

5 Comments

  1. Instead of

    app = described_class.allocate
    app.send :initialize

    You should be able to use

    app = described_class.new!

  2. I’m working on it right now. 2 hours lost trying to do that and you saved my life. I have no idea who you are but you deserve a beer my friend.

  3. Dmitriy Nesteryuik

    July 29, 2014 — 16:33

    If it is difficult to test your code, then you need to rethink your decision about design. You use the trick which may not work with a new version of Ruby or Jruby. Do you want to spend time (money) on maintaining such code? Move logic of your application to own class and test it in isolation.

  4. Sometimes you may want to test if an invocation of a method just delegates to somewhere else. This trick can help wit such case as well.

  5. Ronny Ager-Wick

    November 9, 2014 — 07:33

    any idea how to achieve the same result with minitest::spec? I’m having the exact same requirement using sinatra and minitest, but minitest doesn’t provide described_class, hence this solution doesn’t apply, unfortunately.

Leave a Reply

Your email address will not be published. Required fields are marked *

*

Copyright © 2024 Closer to Code

Theme by Anders NorenUp ↑