One of the tests in a scaffold-generated RSpec controller spec fails, and it looks to me as if it must always fail by design, but of course it is surely supposed to succeed.
I develop a Rails 4 app with RSpec specs generated by rails g scaffold
.
The controller spec for my SkillsController
requires me to fill in a 'valid attributes' hash and an 'invalid attributes' hash for my model, which I did.
The tests all succeed except for "PUT update with invalid params re-render the 'edit' template":
1) SkillsController PUT update with invalid params re-renders the 'edit' template
Failure/Error: expect(response).to render_template("edit")
expecting <"edit"> but rendering with <[]>
# ./spec/controllers/skills_controller_spec.rb:139:in `block (4 levels) in <top (required)>'
In the Rails console, I confirmed that my invalid_params
hash contains invalid parameters ({ hack: 'hack' }
).
The controller calls the skill_params
method which returns an empty hash because my invalid_params
hash contains only invalid parameters.
Calling skill.update(skill_params)
with an empty skill_params
hash returns true, so that the else
part will never execute, and the 'new' template will not be rendered:
def update
respond_to do |format|
if @skill.update(skill_params) # empty hash due to invalid params!
format.html { redirect_to @skill, notice: 'Skill was successfully updated.' }
format.json { render :show, status: :ok, location: @skill }
else
format.html { render :edit }
format.json { render json: @skill.errors, status: :unprocessable_entity }
end
end
end
To summarize: The spec PUTs a hash with invalid parameters to my SkillController. The SkillController's 'skill_params' cleans up this hash, returning an empty hash. skill.update
with an empty hash is a no-op (confirmed on the console), and the method returns true
.
Therefore, the assertion that the 'edit' template should be rendered will never, ever be true, and the default controller spec for the update action with invalid params will never turn green.
What am I missing here?
I finally figured it out and post the solution here, in case someone else finds himself/herself in the same situation.
The 'invalid params' hash is not so much about parameters with invalid attribute names, it's more about invalid attribute values.
Invalid attribute names are simply ignored/discarded, just as @Simone Carletti pointed out in his response.
If invalid values are PUT to the controller, the mymodel.update
method will return false, and the controller redirects to the :edit action.
Thus, in order to make the generated spec pass, the 'invalid attributes' hash at the top of the spec file should be filled with valid attribute names and invalid attribute values (e.g., nil, if an attribute is required).
This is what you should expect. When you use the strong parameters, the returned Hash only contains the attributes you explicitly permit.
This means that if you don't allow the hack
parameter, then if you usmbit a request with
{ hack: 'hack' }
the results of skill_params
will be
{}
By design, update
is perfectly happy to accept an empty Hash. Therefore, if you execute
@skill.update({})
the result is true because the execution is successful. Of course, it didn't do anything, but it didn't fail.
Therefore, your controller will silently succeed and it will not run the edit
condition.
I don't see why you would expect that passing a "not handled" param should cause the controller to show the edit page. It happens every day. Just take a random site and append random query strings to it. For example, go to
http://stackoverflow.com/?tab=interesting
and append a random query string
http://stackoverflow.com/?tab=interesting&foo=bar
The page will happily ignore it. It will not render an error, nor it will crash. The same happens to your controller, the unknown params such as hack
as silently ignored and the controller renders the successful action.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With