Victor Marchuk
Victor Marchuk

Reputation: 13886

RSpec how to properly structure tests?

I'm writing an app with Rails that will have REST API. Most of the controllers aren't accessible unless a user has been authorized. It's done by inserting a method that checks user's privilege in before_action hooks in controllers. I want to test that unauthorized users can not access certain parts of the API. Currently I do it like that:

require 'rails_helper'

RSpec.describe RoomsController, type: :controller do
  ...
  describe "while unauthenticated" do
    before do
      logout
    end

    def expect_unauth
      expect(response).to have_http_status(:unauthorized)
    end

    it "GET #index returns http unauthorized" do get :index; expect_unauth end
    it "GET #show returns http unauthorized" do get :show, {id: 1}; expect_unauth end
    it "DELETE #destroy returns http unauthorized" do delete :destroy, {id: 1}; expect_unauth end
    it "POST #create returns http unauthorized" do post :create, {id: 1}; expect_unauth end
    it "PUT #update returns http unauthorized" do put :update, {id: 1}; expect_unauth end
  end

It works, but it's pretty much the same for every controller. How can I make such a test without copy-pasting this code in every controller? Should I even test for it or since it's a pretty simple I should just assume that it works and write tests for a specific to controller functionality?

Also, does it even belong in controller specs? Maybe it should be a request spec?

Upvotes: 1

Views: 325

Answers (2)

pduey
pduey

Reputation: 3786

Why do you need to test expect_unauth on every single controller action?

I'm assuming you have the auth check in ApplicationController or somewhere common to all controllers. I.e., all API actions requiring authentication respond the same way when there's an unauthorized access. So, it's redundant to test that on every single action. You could write a single spec to test that an unauthorized access to the system responds as expected.

In the examples where the functionality of the action is being tested, the system first checks to ensure that there is a valid login, and if there isn't, the next expectation in your test will fail.

To answer the question of should you be using controller specs or request specs, take a look at rspec_api_documentation. It combines request specs with a documentation generator.

If that's overkill for you, then I think for API testing it's a fine line between a controller spec and a request spec. Controller specs stub the view, so if you want to check something in the actual response, e.g., that the JSON has something specific in it, then you would use request specs.

Upvotes: 1

pierallard
pierallard

Reputation: 3371

I usually had this code for the same behavior :

ACTIONS = [
  [ :index, :get, {} ],
  [ :show, :get, { :id => 1 }],
  [ :delete, :destroy, { :id => 1 }]
]

ACTIONS.each do |action, method, params|
  it "shout return unauthorized when #{method} #{action}" do
    send(method, action, params)
    expect_unauth
  end
end

Upvotes: 1

Related Questions