3

I've got a series of RSpec tests for a Sinatra based API, and would like to refactor them to make them a little simpler and reduce repetition.

Here's an example a test for a route:

describe 'post /sections with empty data' do
    before do

        params = {
            :site_id    => site.id,
            :page_id    => page.id,
        }

        post '/sections', params, @session
    end

    specify { last_response.status.should == 200 }
    specify { json_response['id'].should_not be_nil }
    specify { json_response['type'].should == default_section_type }
end

Each test will be using the same base URL, with the same session data, the only difference is the parameters, and what the responses should be. There's at least 4 tests (GET, POST, PUT, DELETE) per route, and usually more.

Is there a way of making these tests more manageable?

djlumley
  • 2,955
  • 2
  • 24
  • 30

2 Answers2

3

Without resorting to metaprogramimng, you can use nested describe blocks to override only the parameters you want:

describe "/sessions" do
  before do
    send(http_method, "/sessions", params, @session)
  end

  describe "with POST" do
    let(:http_method) { :post }

    describe "and empty data" do
      let(:params) do
        { :site_id => site.id, :page_id => page.id }
      end

      specify { last_response.status.should == 200 }
      specify { json_response['id'].should_not be_nil }
      specify { json_response['type'].should == default_section_type }
    end

    describe "with non-empty data" do
      let(:params) do
        # relevant params
      end
    end
  end

  describe "with GET" do
    let(:http_method) { :get }

    # ...
  end
end
Renato Zannon
  • 28,805
  • 6
  • 38
  • 42
  • +1 For the information that was given I think this is the best way to do it. – Ismael Abreu Oct 03 '12 at 23:13
  • At the moment I am using nested describe blocks for the routes, and then the tests underneath them, but it makes sense to abstract the route into a before block as you've done. Thanks! – djlumley Oct 03 '12 at 23:40
  • 1
    I've moved the before filter outside into my outermost describe that describes the API, and have allowed each POST/GET/PUT/DELETE block to describe the URL it's using so that it's easy to specify `/sections/#{section.id}`. Thanks very much for the idea! – djlumley Oct 04 '12 at 04:39
1

Have no idea if this works but it can give you an idea of what you can do

describe ' /sections with empty data' do
    before(:all) do
      @params = {
            :site_id    => site.id,
            :page_id    => page.id,
        }
    end

    after(:each) do
      specify { last_response.status.should == 200 }
      specify { json_response['id'].should_not be_nil }
      specify { json_response['type'].should == default_section_type }
    end

    [:get, :post, :put, :delete].each do |http_method|
        it "works with #{http_method}" do
            send(http_method) '/sections', @params, @session
        end
    end
end

Update

Reading your question again made me realize that this is not what you actually asked for. If it doesn't help at all tell me so I delete it.

Community
  • 1
  • 1
Ismael Abreu
  • 16,443
  • 6
  • 61
  • 75
  • Ideally I'd test different responses, but I like the idea of condensing the tests using an each block. – djlumley Oct 03 '12 at 23:38
  • Yes, but for your case you would put also the params in the array in a hash like `[{method: 'post', params: {...}}, ...]` and that would get messy and ilegible. Renato's answer seems good to go. And maybe after you can try refactoring it – Ismael Abreu Oct 03 '12 at 23:42