I faced the same problem reported in the question post while using following versions
ruby 2.3.2
rails (5.0.0.1)
rspec-rails (3.5.2)
Searching for the problem on web I found a related issue at https://github.com/rails/rails/issues/26069 and the solution suggested by it is to pass as: :json option to the post, get etc methods while using them in the controller tests (refer the PR referenced in comment https://github.com/rails/rails/issues/26069#issuecomment-240916233 for more details). Using that solution didn't solved the params mingling issue I was encountering. Following were the results found for different types of data I used with the recommended solution:
In my controller spec I have following
before(:each) do request.accept = "application/json" end
and in the test logs I do see that the request is being served
Processing by Api::V1::MyController#my_action as JSON
Data-1
data = [ { param_1: "param_1_value", }, { param_2: "param_2_value", } ] params.merge!(my_data: data) post :my_action, params: params, as: :json
That ends-up in request params as following
{ "my_data"=> [ {"param_1"=>"param_1_value", "param_2"=>"param_2_value"} ] }
which is wrong.
Data-2
data = [ { param_1: "param_1_value", something_else: "" }, { param_2: "param_2_value", another_thing: "" } ] params.merge!(my_data: data) post :my_action, params: params, as: :json
That ends-up in request params as following
{ "my_data"=> [ {"param_1"=>"param_1_value", "something_else"=>"", "another_thing"=>"", "param_2"=>"param_2_value"} ] }
which is wrong.
Data-3
data = [ { param_1: "param_1_value", param_2: "" }, { param_1: "" param_2: "param_2_value", } ] params.merge!(my_data: data) post :my_action, params: params, as: :json
That ends-up in request params as following
{ "my_data"=>[ {"param_1"=>"param_1_value", "param_2"=>""}, {"param_1"=>"", "param_2"=>"param_2_value"} ] }
which is correct.
It should be noted that for Data-3 without the as: :json option also I receive the correct data in request params.
One more thing: In comment https://github.com/rails/rails/issues/26069#issuecomment-240358290 an alternate solution suggested to deal with the problem narrated above is following
another fix would be to specify nested attributes not as array but as hash:
params = { id: @book.id, book: { title: 'Cool', pages_params: { "0" => { id: @page.id, content: 'another content' }, "1" => { id: @delete_page.id, _destroy: 1 }, "2" => { content: 'another new page' } } }, format: :json }
Unfortunately this was removed from the documentation of nested attributes so I don't know if this is going to stay valid. http://api.rubyonrails.org/classes/ActiveRecord/NestedAttributes/ClassMethods.html
But this solution has a drawback is that we need to manually sanitize the data on controller-end to bring it back to expected structure i.e. Array of Hashes.
Finally I am sharing below what worked for me
spec/shared_contexts.rb
RSpec.shared_context "common helpers" do def set_request_header(request_obj:, request_header_name:, request_header_value:) request_obj.headers[request_header_name] = request_header_value end def set_request_header_content_type_as_json(request_obj:) set_request_header(request_obj: request_obj, request_header_name: 'CONTENT_TYPE', request_header_value: 'application/json') end end
Then in my spec file
require 'shared_contexts' RSpec.describe Api::V1::MyController, :type => :controller do include_context "common helpers" context "POST #my_action" do it "my example" do data = [ { param_1: "param_1_value", }, { param_2: "param_2_value", } ] params.merge!(my_data: data) set_request_header_content_type_as_json(request_obj: request) post :my_action, params: params end end end
As can be seen setting the request header CONTENT_TYPE was what was missing to make the request params to be received in expected structure.