When developing your test suite via request spec, you may want to test functionality of controllers which do not have any actions. These controllers exist for sharing common functionality between multiple controllers.
While writing these tests via controller
testing is more straight forward, I couldn't find any direct docs for such when using request
testing.
Let's get to it then. Lets assume that we have a controller called BaseController.rb
that looks like the following
class BaseController < ActionController::API
private
def current_company
return "forem" if request.headers['X-Api-Token'].present?
return "external"
end
end
For the sake of the article we made a simple method that returns the current company as Forem if url, contains string 'forem' or else simply return "external"
Lets test this out. In our rspec file, base_controller_spec.rb we use the following code
describe 'Sets company', type: :request do
before do
klass = Class.new(BaseController) do
def index
json_response(current_company, :ok))
end
end
stub_const('TestController', klass)
Rails.application.routes.disable_clear_and_finalize = true
Rails.application.routes.draw do
get '/test', to: 'test#index'
end
end
after { Rails.application.reload_routes! }
end
Here's what we did
- In the before block define a new anonymous Class. Animus classes are standard ruby classes without a constant assigned to them. They are very helpful for writing maintainable test.
-
Class.new(BaseController)
is a form of inheritance in ruby. - Inside this anonymous class we define a new method/action which returns a simple json response. Via this response we can test if our base controller method works as expected.
- We use
stub_constant
which helps us to prevent leaking of this class to other spec files. - We preserve all original routes via
Rails.application.routes.disable_clear_and_finalize = true
- Then create a new route endpoint to hit our test. This route points to our newly create controller action defined inside the anonymous class
- Once all our tests are run we make sure to return our routing table to its original state.
Lets get to our tests. Should be self explanatory
it 'when api token is given' do
get '/test', headers: {'X-Api-Token': 'random_test'}
expect(response.body).to eq("forem")
end
it 'when api token is not given' do
get '/test', headers: {}
expect(response.body).to eq("external")
end
This simple temple should hope you to solve more complex problems for testing base controllers. A full discussion can be found here
Top comments (0)