DEV Community


Posted on

Anonymous Controller tests for Request Spec

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
   def current_company
     return "forem" if request.headers['X-Api-Token'].present?
     return "external"
Enter fullscreen mode Exit fullscreen mode

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 = do
      def index
         json_response(current_company, :ok))
    stub_const('TestController', klass)

    Rails.application.routes.disable_clear_and_finalize = true 

    Rails.application.routes.draw do
      get '/test', to: 'test#index'

  after { Rails.application.reload_routes! }

Enter fullscreen mode Exit fullscreen mode

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.
  • 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")

it 'when api token is not given' do
  get '/test', headers: {} 
  expect(response.body).to eq("external")
Enter fullscreen mode Exit fullscreen mode

This simple temple should hope you to solve more complex problems for testing base controllers. A full discussion can be found here

Discussion (0)