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.