Rspec-rails: Nil parameter unexpectedly coerced as empty string

Created on 4 Sep 2018  路  8Comments  路  Source: rspec/rspec-rails

What Ruby, Rails and RSpec versions are you using?

Ruby version: 2.5.1
Rails version: 5.2.1
Rspec version: 3.8

Observed behaviour

When a query parameter is passed as nil, the query is passed to Rails and coerced as an empty string.

Expected behaviour

Expected that params: { query: nil } be passed without a query parameter, and tests looking for empty strings (e.g. /users?query= ) must explicitly test with an empty string in those cases. This is especially useful for using lazily evaluated let syntax to add/remove parameters without having to rewrite them.

Can you provide an example app?

Here: https://github.com/etgrieco/rspec-rails-nil-params-example. Test expected to fail is passing.

UsersController

  def index
    escaped_query = CGI.escape(params[:query]) # CGI.escape(nil) should yield TypeError
    render(json: { query: escaped_query })
  end

UsersController Spec

  before { get :index, params: params }
  subject { JSON.parse(response.body) }

  let(:query) { "this is a query" }
  let(:params) { { query: query } }

  context 'with a query' do
    it { should eql("query" => "this+is+a+query") }
  end

  context 'without a query' do
    let(:query) { nil }

    it { should eql("query" => '') } # This spec is unexpectedly passing
  end

All 8 comments

Hello, this is how Rails behaves, you can't ever have a nil parameter passing through, http://myapp/path/to/controller?query= would come through as { "query" => "" } if you want nil as a value you need to omit the key e.g. http://myapp/path/to/controller which should make params { }

@JonRowe Going to the route http://myapp/path/to/controller?query will indeed produce nil params. You can have a nil parameter pass through:

Nil param

Started GET "/users?query"
Processing by UsersController#index as HTML
  Parameters: {"query"=>nil}
Completed 500 Internal Server Error in 0ms (ActiveRecord: 0.0ms)

TypeError (no implicit conversion of nil into String):

Empty string

Started GET "/users?query=" for 127.0.0.1 at 2018-09-05 11:58:02 -0400
Processing by UsersController#index as HTML
  Parameters: {"query"=>""}
Completed 200 OK in 0ms (Views: 0.2ms | ActiveRecord: 0.0ms)

I believe a controller test with params: { "query" => nil } should behave the same way as if the parameters were actually {"query"=>nil}, as shown above.

EDIT: Similar issue documented here, although I don't know how related it is: https://github.com/rails/rails/issues/28129

Thats because GET "/users?query" is invalid, query here isn't a param, its just junk.

You can't recreate this in Rails test mode. Everything is expected to be a string.

(This is not RSpec's doing, it's all Rails, we pass things through as you give them to us)

@JonRowe Thanks for the clarification, not sure whether was happening in Rails or RSpec -- definitely unexpected though when Rails, within the controller, clearly can differentiate between nil and string params when going to the route -- so if this change were to happen it would need to happen on the Rails testing end I guess?

You're missing the point, query is not a nil param, it's just an invalid string, valid url params will always have = and thus will always be strings, if you raise this with the Rails team I suspect they will fix it so /users?query returns a params of {} which is the technically correct version.

For the record and anyone else who finds this issue. You can prevent this situation like this:

before { get :index, params: params, as: :json }

Seen here https://stackoverflow.com/a/44704018/1407371

Was this page helpful?
0 / 5 - 0 ratings