Rails function test: sending URL request parameters to a POST request

I send a POST request in a Rails functional test as follows:

post :create, collection: { name: 'New Collection' } 

collection sent as form data in JSON format as expected.

I cannot figure out how to add a request to a URL. The documentation says that I can access the request object and modify it before submitting. So I tried this:

 @request.GET[:api_key] = 'my key' post :create, collection: { name: 'New Collection' } 

But :api_key never appears in the request.GET hash on the server. (This happens when I send it to another HTTP client.)

+6
source share
5 answers

A bit of background to clarify the situation: although the request cannot be both GET and POST, nothing stops using it as query strings and body shape data when using POST. You can even have a POST with all parameters in the query string and an empty body , although this sounds pretty unusual.

Rails supports this script, and you can easily submit a form using a POST request and still have the request in the form action. The request will be available with the request.GET hash (which is the query_string alias), while the POST body with the request.POST hash parameter (the request_parameters alias). The params hash is actually built from combined GET and POST hashes .

However, from my research, it seems that Rails does not support passing request strings in POST requests in functional controller tests . Although I could not find anything about this in any documentation or among the known issues on github , the source code is pretty clear. In the following text, I assume that you are using Rails 4.

Why it does not work

The problem with functional controller checks is that they do not use real requests / responses, but mimic the HTTP hash code: the request is tricked, its parameters are filled in the appropriate places, and this controller action is simply called the normal ruby ​​method. All this is done in the action_controller/test_case .

As it turned out, this simulation does not work in your particular case for two reasons:

  • The parameters passed when the test was run are always passed either to request_parameters , i.e. hash request.POST when using a POST request or to query_string (i.e. request.GET ) for GET test requests. It is not possible to set both of these hashes during the same test run.

    It really makes sense since GET , POST , etc. helpers in functional tests accept only one hash of parameters, so the internal test code cannot know how to split them into two hashes.

  • It is true that you can request a query before running the test using the @request variable, but only to a certain extent can you set headers, for example. But you cannot set the internal attributes of the request because they are processed during the test run. This recycling is done here , and it resets all the internal variables of the request object and the base rack request object. Therefore, if you try to configure GET request parameters, such as @request.GET[:api_key] = 'my key' , this will have no effect, since the internal variables representing this hash will be erased during disposal.

Solutions / Workarounds

  • Give up functional testing and choose integration tests instead . Integration tests allow you to set the environment variables of the rack separately from the main parameters. The following integration test passes the variable ente tv2 query_string , in addition to the usual body post parameters, and should work flawlessly:

     class CollectionsTest < ActionDispatch::IntegrationTest test 'foo' do post collections_path, { collection: { name: 'New Collection' } }, { "QUERY_STRING" => "api_key=my_api_key" } # this proves that the parameters are recognized separately in the controller # (you can test this in you controller as well as here in the test): puts request.POST.inspect # => {"collection"=>{"name"=>"New Collection"}} puts request.GET.inspect # => {"api_key"=>"my_api_key"} end end 

    You can use most functions from functional tests in your integration tests. For instance. you can check the assigned instance variables in the controller with the assigns hash.

    The transition argument is also supported by the fact that Rails 5 will refuse functional controller tests in favor of integration testing and since Rails 5.1 supports these functional tests, they will be moved to a separate stone.

  • Try Rails 5: although the functional tests will be deprecated, its source code seems to have been heavily rewritten in the master rail and for example reusing the query is no longer used. Therefore, you can try and try to set the internal query variables during the installation of the test. I have not tested it though.

  • Of course, you can always try to disable a functional test so that it supports separate parameters for the query_string and request_parameters hashes, which must be defined in the tests.

I would go along the route of integration tests :).

+14
source

I assume that the controller is named CollectionsController , and its route to the create action is /collections (if not, you just need to follow the example below)

And I also assume that you are in spec request

This should work:

 post '/collections?api_key=my_key', collection: { name: 'New Collection' } 
+2
source

The second argument to post is the hash of all the parameters that you will receive in the controller. Just do the following:

 post :create, collection: { name: 'New Collection' }, more_params: 'stuff', and_so_on: 'things' 

These parameters will be available in the controller:

 params[:and_so_on] == 'things' 
+1
source

You want to send a POST request:

I send a POST request in a Rails functional test as follows:

But you want to get data from a GET request:

But :api_key never appears in the request.GET hash on the server.

The request cannot be GET and POST at the same time, if you send a POST request and pass parameters in the query string, then you should have these parameter values ​​in the POST request, GET just won’t receive anything.

Then:

 @request.GET[:api_key] = 'my key' post :create, collection: { name: 'New Collection' } 

You change the GET values ​​in the request, but then you actually send the POST request, which means that when you call the post method and send the request to the server, only what you sent to POST is available, Just send the api key complete with the POST request (maybe be inside the hash of the collection).

+1
source

This is also a problem when testing POST actions with RSpec (v3.4).

The workaround is to mock the return value of the request.GET or request.query_string methods.

 it "should recognise a query parameter in post action" do allow(subject.request).to receive(:query_string).and_return("api_key=my%20key") @params = {collection: { name: 'New Collection' }} expect(subject.request.query_string).to eq "api_key=my%20key" post :create, @params end 
0
source

Source: https://habr.com/ru/post/970805/


All Articles