Ruby version: 2.5.1
Rails version: 5.2.1
Rspec version: 3.8
When a query parameter is passed as nil, the query is passed to Rails and coerced as an empty string.
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.
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
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 }