Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

RSpec controller spec fails on update with invalid params

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?

like image 616
bovender Avatar asked Dec 11 '15 18:12

bovender


2 Answers

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

like image 195
bovender Avatar answered Nov 17 '22 14:11

bovender


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.

like image 3
Simone Carletti Avatar answered Nov 17 '22 15:11

Simone Carletti