Reputation: 3992
I am new to Rails and I want to test my set strong parameters of the Book model with a controller test. I am using Minitest and Rails 4.
Book model:
class Book < ActiveRecord::Base
validates :title, presence: true, length: { in: 1..150 }
end
Book controller wit params:
def create
@book = Book.new book_params
if @book.save
redirect_to action: "index", notice: 'Success.'
else
render :new
end
end
private
def book_params
params.require(:book).permit(:title, :cover_image)
end
My idea for a test - does fail, because it creates an entry:
assert_no_difference('Book.count') do
post :create, book: {
id: 123,
title: "Lorem ipsum"
}
end
How can I get the tests go green and is it correct to test the strong parameters with a controller test?
Upvotes: 2
Views: 1228
Reputation: 1088
I do like to test Strong Parameters in the controller. I also like to test them more directly, so this is how I do it.
First, I have a test helper that is required in my test/test_helper.rb
file:
test/test_helpers/controller_strong_params_helper.rb
# frozen_string_literal: true
module ControllerStrongParamsHelper
def assert_requires_param(param, &block)
@controller.params = ActionController::Parameters.new()
assert_raises(ActionController::ParameterMissing) { yield }
@controller.params = ActionController::Parameters.new(stub_parameter: {})
assert_raises(ActionController::ParameterMissing) { yield }
# It's not enough to have an empty required parameter, there needs to be something inside.
@controller.params = ActionController::Parameters.new(param => {})
assert_raises(ActionController::ParameterMissing) { yield }
@controller.params = ActionController::Parameters.new(param => '')
assert_raises(ActionController::ParameterMissing) { yield }
@controller.params = ActionController::Parameters.new(param => {something_inside: 'something'})
assert_nothing_raised { yield }
end
end
This lets me easily test the strong params that are not optional.
Now assume I have these strong params in my ExampleController
:
def example_params
params.require(:example).permit(:id,
:name,
:description)
end
private :example_params
This is what my minitest tests would look like:
test/controllers/example_controller_test.rb
###############################################
test '#example_params should require an example parameter' do
assert_requires_param(:example) { @controller.send(:example_params) }
end
###############################################
test '#example_params should permit some expected example parameters' do
# Using hash rockets so the equality check works.
expected_permitted_params = { 'id' => nil,
'name' => nil,
'description' => nil }
# Specifically merge in any potential non-attribute parameters here if present/needed.
all_params = { example: Example.new.attributes }
@controller.params = ActionController::Parameters.new(all_params)
actual_permitted_params = @controller.send(:example_params)
assert_equal(expected_permitted_params, actual_permitted_params)
end
Upvotes: 0
Reputation: 2192
I am looking for an answer to almost the same question. When using Rails 5 I eventually came up with a solution (call it workaround if you like :-) for testing that the unwanted params don't actually get through. In my (simplified here) case I want to disallow some "security critical" params being passed through when creating a new user.
In the controller (permitting only email
and password
):
private
def user_params
params.require(:user).permit(:email, :password)
end
In the integration test:
test "not permitted signup data submitted" do
new_user_email = "tester_" + (0...10).map { ('0'..'9').to_a[rand(26)] }.join + "@testing.net"
get signup_path
assert_difference 'User.count', 1 do
post signup_path, params: { user: { email: new_user_email, password: "testpassword", role_id: 1 } }
end
user = User.last
assert user.email == new_user_email
assert user.role_id == nil
end
Here I submit an additional, "sensitive" parameter role_id
with the value of 1
(admin). I expect the user to be created. Then I read that newly (last) created user and expect it to have role_id
empty (nil
). To make the test fail I add :role_id
to user_params
. Removing it, makes the test pass. Obviously if your attribute can't be nil (aka NULL in SQL), you can test for default value being stored instead of the submitted one.
Upvotes: 3
Reputation: 3992
Since Rails drops all unpermitted parameters not in permit
, the new record will be created, hence the test will be red.
Although, one can raise an exception with the action_on_unpermitted_parameters
method when non-whitlisted parameters are submitted.
Upvotes: 2