2
\$\begingroup\$

I tend to write quite explicit tests for my rails controllers exposing APIs with two seperate concerns:

  • meeting the defined API-Response with headers headers and the response itself
  • ensuring the right calls to underlaying methods/objects are made

However when the calls are not simply executable in a test the controller tends to get bloated. This is due to the fact that when the calls are stubbed out the execution order of the two test concerns is reversed:

  • for the api calls:
    • first stub the call method (since we don't want this to be run)
    • make the request
    • test the response
  • for the method calls
    • define the receive-expectations
    • make the request

Example Code:

require 'spec_helper'

describe ItemsController do
  render_views

  shared_examples :empty_success_response do
    it { expect(response.status).to eql(202) }
    it { expect(response.content_type).to eql('application/json')}

    it 'returns error message' do
      json = JSON.parse(response.body)
      expect(json).to be_blank
    end
  end

  context 'a single item' do
    subject { FactoryGirl.create(:item) }

    describe '#destroy' do
      let(:do_action) do
        delete :destroy, id: subject.id.to_s
      end

      describe 'response' do
        before do
          controller.stub!(:destroy_item)
          do_action
        end

        it_behaves_like :empty_success_response
      end

      it 'makes the call' do
        expect(controller).to receive(:destroy_item).once.times.with(subject.id.to_s)
        do_action
      end
    end

    # ...

  end

end

Note: The destroy_item method is just a stand in, the actual method makes more sense ;)

The code in the example works nicely, and is even fairly dry (mostly because the request is moved into a let), but feels still very chunky and bloated - especially if we consider this is just one simple actions, with a usual controller having quite a few more real logic to test.

Does anyone have an idea how to make this code more readable?

Or does it maybe make sense to split the 2 kinds of tests completely? This would however mean quite a bit of duplication since we have the same caller-code in 2 places.

\$\endgroup\$
1
\$\begingroup\$

shared examples share the let scope, so you could further dry up your code (assuming you use do_action for every action) to this:

shared_examples :empty_success_response do

  before do
    do_action
  end

 ...
end

Also, if you are using do_action, I also like to add to my scopes:

describe '#destroy' do
  let(:do_action) do
    delete :destroy, id: subject.id.to_s
  end

  after do
    do_action
  end

  ...
end

This way, you don't have to call do_action if all you do in the test is set expectations.

it 'makes the call' do
  expect(controller).to receive(:destroy_item).once.times.with(subject.id.to_s)
end

If in some tests you want do_action to run before the end of the test - that's no problem, since let runs the code only once - so it won't run again at the end of that specific test!

\$\endgroup\$

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.