skip to main content

How to Test Code Shared by Controllers and Helpers in Rails

Using view_context to test a method's presence in controllers and helpers in Rails

Alex Morton

February 4, 2021

Recently, I took on the task of refactoring some repetitive code that occurred several times throughout our codebase into a neat method. In other words, I made the code a bit more DRY (“Don’t Repeat Yourself” - a well-known convention in writing good code).

Essentially, the method (feature_enabled?), checks to see if a new feature has been enabled within the Orbit app for a user or their workspace.

Using the Flipper gem for feature flags

In this case, we used a gem called Flipper to help us turn on feature flags for certain users before implementing big feature changes to all of our users at once.

Here’s how the code looked before the refactored method:

if Flipper[:my_feature].enabled?(current_user) || 
  Flipper[:my_feature].enabled?(@workspace)
    # do something cool
end

And here’s how the same code would look with the feature_enabled? method:

if feature_enabled?(:my_feature, current_user, @workspace)
    # do something cool
end

Moving the method out of Application Helper and into Application Controller

When I first defined feature_enabled?, I did so in the application_helperfile. Consequently, the corresponding unit tests were written in application_helper_spec.

This method, however, needs to be used in both the helpers and the controllers. As I was working my way through this refactor, I started to run into an issue where the helper method wasn’t being recognised anywhere it was called in any controller files.

As I tried to work through it, I had a couple of different ideas as to what was going on:

  1. It wouldn’t be possible to call the new method from the controllers, so I'd have to duplicate it or leave things as they were or
  2. I needed to move the feature_enabled? method into the Application Controller so that the controllers have access to it

Spoiler alert! It ended up being the second option. So, I ended up transferring some code into different files by moving:

  1. The helper function (feature_enabled?) out of the Application Helper and into the Application Controller
  2. The corresponding unit test out of the Application Helper spec and into the Application Controller spec

Here’s what the function looks like in application_controller:

# frozen_string_literal: true
class ApplicationController < ActionController::Base
 
 helper_method :feature_enabled?
 
 protected
 
  def feature_enabled?(feature, user, workspace)
    Flipper[feature].enabled?(user) || Flipper[feature].enabled?(workspace)
   end
end


And here’s the corresponding application_controller_spec:

RSpec.describe ApplicationController, type: :controller do
 let(:controller) { described_class.new }
 
  describe '#feature_enabled?' do
     let(:user) { create(:user) }
     let(:workspace) { create(:workspace) }
     let(:another_user) { create(:user) }
 
     subject { controller.send(:feature_enabled?, :my_feature, user, workspace) }
     
     it 'returns true if feature is enabled for everybody' do
       Flipper.enable(:my_feature)
       expect(subject).to be true
     end
 
     describe do
       context 'when feature is not enabled' do
         it { expect(subject).to be false }
 
         context 'when feature is enabled for this user' do
           before { Flipper.enable_actor(:my_feature, user) }
           it { expect(subject).to be true }
         end
       end
     end
     it 'returns true if feature is enabled for everybody' do
       Flipper.enable(:my_feature)
       expect(subject).to be true
     end
   end

Testing the method’s presence in the helpers and views

I think it was here that things were starting to come together for me, but it didn’t seem that the tests were accurately testing our feature_enabled? method.

By this point, we’d written the specs that proved feature_enabled? did what it is supposed to. However, we still needed to prove that the method would be available in the helpers, and therefore the views.

(That peace of mind is important, as someone could easily remove the helper_method :feature_enabled? call in application_controller by mistake and consequently break any usage of it in the helpers and views.)

To that extent, we needed to write a test that proved this method would indeed be available in the helper and view files.

My first approach was to try and write a test in application_helper_spec to verify the presence of the method, but it turns out that the Application Controller helper_methods are only added to the helpers during a real request/response cycle.

The application_helper_spec doesn’t simulate that - instead, it just calls a plain old object, meaning that feature_enabled? wasn’t defined when I tried to call it there.

I also realized it is the controller's (not the helper's) job to make sure the method exists in the helpers. After all, the helper_method call is located in the controller.

So once I realized that, it hit me that the application_helper was the wrong place for this spec. No wonder it was so difficult to test there!

Using view_context to test our helper method in the controller

From there, I went looking for a way to test our feature_enabled? method in the controller. This was the StackOverflow solution:

You can call any helper methods from a controller using the view_context, e.g. View_context.my_helper_method

With this next step in the right direction, I added a test that would specifically test whether the view_context of the controller had the helper method defined. I used the respond_to RSpec matcher for this, which calls respond_to? on the object to see if the method is defined:

describe '#view_context' do
   subject { controller.view_context }
   it { expect(subject).to respond_to(:feature_enabled?) }
 end

Because of how view_context works in Rails, we can be sure that any methods it defines will be available in our helpers and views.

Conclusion

In conclusion, that’s how I figured out how to test a helper method from the Application Controller in Rails.

I removed the method from the Application Helper and transferred it to the Application Controller, and was then able to successfully test the behavior of the method in the Application Controller spec.

I also added a test to prove the feature_enabled? method would be available to helpers and views.

Thanks so much for reading! Feel free to stay in touch with us on Twitter (@OrbitModel).


You might also like:

Why Orbit is Better Than Funnel for Developer Relations
DevRel teams need tools and models created specifically for our discipline, and not just those adopted from other fields.
Slack vs Discord vs Discourse: The best tool for your community
An in-depth comparison of 3 top community platforms across dozens of factors.
How we use Orbit to build Orbit
A guide to how we use our product to build our community.

Join our mailing list  📩

No spam, just insights, product and content updates, event invites, and the occasional space pun.

Orbit is a registered trademark of Orbit Labs, Inc.

Join us in Orbit.


In early access, we’re working closely with our early users to setup their implementation and get the most out of Orbit. Complete this form and we’ll be in touch soon.

nevermind