Last Updated: December 26, 2022
· treble37

Using ShouldaMatchers#permit To Ensure Your Controller Parameters Get Passed


I was working on a Rails 4.2.x application that had a controller with nested parameters. The whitelisted parameter list was so long, and I was making changes fairly fast without having a good way to ensure that I was passing all the data (via the parameter attributes) that I wanted to save.

For example, when adding a new piece of data (such as a person's school), I found myself forgetting to allow the application to save a person's school simply because I forgot to include it in the whitelisted parameter list.

This could easily have resulted in some data loss. I wanted a "Test Driven Development" (TDD) approach to ensure I wouldn't forget those parameters.

TDD via ShouldaMatchers#permit

Fortunately, there's a gem shoulda-matchers that is tailor made for this use case.

Here is the test setup. I actually have many more nested parameters besides user_demographic_attributes, but for readability's sake I omitted them.

The test setup assumes you are using RSpec with factory_girl.

Now you don't have to worry about whether or not you forgot to

Controller Setup: people_controller.rb

class PeopleController < ApplicationController

 # Only allow a trusted parameter "white list" through.
  def person_params
    params.require(:person).permit(:id, :commit, :outcome_status_description, :additional_feedback, user_demographic_attributes: [:first_name, :last_name, :email, :school, :title, :department, :affiliation, :secondary_department, :secondary_school, :_destroy])

RSpec Controller Test Setup: peoplecontrollerspec.rb

RSpec.describe PeopleController, :type => :controller do

before do
@person = create(:person)
@survey = create(:survey, year: "2015", person_id:, status: "Saved")
@user_demographic = create(:user_demographic, person_id:

it do
  params = {
    person: {
      user_demographic_attributes: {
        first_name: "MyString",
        last_name: "MyString",
        email: "MyString",
        school: "MyString",
        department: "MyText"

should permit(:id, :commit, :outcome_status_description, :additional_feedback, user_demographic_attributes: [:first_name, :last_name,  :email, :school, :department, :affiliation, :_destroy]).for(:update_survey, params: params, verb: :patch).on(:person)

In spec_helper.rb add the following:

RSpec.configure do |config|
   #your usual rspec configuration stuff

 Shoulda::Matchers.configure do |config|
   config.integrate do |with|
      with.test_framework :rspec
      with.library :rails