I have the following:
#request.env['RAW_POST_DATA'] = data
#request.env['CONTENT_TYPE'] = 'application/xml'
#request.env['HTTP_CONTENT_TYPE'] = 'application/xml'
post "create", :api_key => api_key, :format => "xml"
and test.log shows this:
Processing ****Controller#create to xml (for 0.0.0.0 at 2011-07-08 15:40:20) [POST]
Parameters: {"format"=>"xml", "action"=>"create", "api_key"=>"the_hatter_wants_to_have_tea1", "controller"=>"****"}
Which... I guess is fine, but the RAW_POST_DATA doesn't show up as a hash in the parameters list in the log.... now... it works when I call the action from the terminal using curl:
curl -H 'Content-Type: application/xml' -d '<object><name>Das Object</name></object>' http://notAvailableDuringTesting.butWorksInDevelopmentMode.dev/object.xml?api_key=the_hatter_wants_to_have_tea1
what am I doing wrong here?
Is there a reason why you aren't just passing the params to the post call itself? eg:
post "create", :api_key => api_key, :format => "xml", :params => data
Controller tests are there to test that the controller action does what it expects when you send it params that you already know of. they generally aren't there to test parsing of xml into params.
If you just want to test the former - then don't bother making them into xml - just pass them as a hash.
If you really do want to test parsing of xml into params - you may need to look into something that operates outside of the scope of rails tests (eg selenium or watir)
Related
I am trying to send some raw data in a JSON post request to my RSpec controller test. I have successfully tested the controller with an actual Postman request but I can't get the Rspec test to work
The error I am getting is
param is missing or the value is empty: annotations
Here is my test set-up, which I have copied verbatimly from rails server log when I run the request via Postman
params = '{annotations"=>[{"id"=>1, "location_start"=>1, "location_end"=>3, "source_text"=>"what", "reading"=>"cool"}, {"id"=>2, "location_start"=>1, "location_end"=>-1, "reading"=>"cool"}]}'
patch :update, params, format: :json
I have also tried
params = {:annotations => [{ :id=>1, :location_start=>1, :location_end=>3}]}
patch :update, params.to_json
https://www.relishapp.com/rspec/rspec-rails/docs/controller-specs/controller-spec#setting-a-different-content-type-for-example-json-(request-type)
Check out how the json body is defined in this example: It's a ruby hash! So you need to not try passing in a raw string but a Hash that then gets automatically converted to JSON for you.
Hope that helps you out
This is a problem that has been bothering me for some time. I am building an API function that should receive data in json and response in json. My controller tests run fine(Since I abstract that the data gets there already decode from JSON and only the answer needs to be interpreted ).
I Also know that the function runs fine since I have used curl to test it with JSON arguments and it works perfectly.
(ex: curl -i --header "Accept: application/json" --header "Content-Type: application/json" -d '{"test":{"email":"andreo#benjamin.dk"}}' )
But obviously I would like to write request(feature) tests to test this automatically and the way I see it they should work exactly like curl, i.e., hit my service like it was an external call. That means that I would like to pass the arguments in JSON and receive an answer. I am pretty lost since all the examples I can see people treat arguments as it was already decoded.
My question is: I am following a wrong premise in wanting to send the arguments and request as a JSON one since i will be testing that rails works, because this is its responsibility? But I would like to see how robust my code his to wrong arguments and would like to try with JSON.
something of this type:
it "should return an error if there is no correct email" do
params = {:subscription => {:email => "andre"}}
post "/magazine_subscriptions", { 'HTTP_ACCEPT' => "application/json", 'Content-Type' => 'application/json', 'RAW_POST_DATA' => params.to_json }
end
Do you know how this is possible? and please let me know if you think I am testing it wrong.
all the best,
Andre
I found my answer on a post here(RSpec request test merges hashes in array in POST JSON params), I think what I was doing wrong concerned the arguments to the request.
so this worked:
it "should return an error if there is no correct email" do
params = {:subscription => {:email => "andre"}}
post "/magazine_subscriptions", params.to_json, {'ACCEPT' => "application/json", 'CONTENT_TYPE' => 'application/json'}
end
describe '#create' do
let(:email) {'andre'}
let(:attrs) {{email: email}}
let(:params) {{format: :json, subscription: attrs}}
it "should return an error if there is no correct email" do
post "/magazine_subscriptions", params
end
end
This is basically what I want to do, with the params given in a form, I want to do a GET/POST request to a site, this site expects an specific URL like http://site.com/user=XXX&size=XXX and it will give me back a JSON, I want to parse/save the data from this JSON into my rails app when the form is submitted.
I am totally lost with this manner, anything would be very appreciated.
Rails Form Data => Build the URL => Do a GET/Post request => Catch JSON => Parse => Save
for rest api you can use activeresource in your application
http://api.rubyonrails.org/classes/ActiveResource/Base.html
if it's something very specific you can use Net::Http to make requests and then parse json to ruby objects by yourself.
Examples of using http://www.rubyinside.com/nethttp-cheat-sheet-2940.html
for decoding json you can use
Json or ActiveSupport::JSON.decode or this https://github.com/flori/json
I guess you want to do a request to another not your site to get the response. So you can install curb gem (the curl wrapper in ruby). Then use it to make the request on another site and parse json with standart RoR tools like Json to hash.
From http://www.rubyinside.com/nethttp-cheat-sheet-2940.html you get you can do the following:
at the top of your file add:
require "net/http"
require "uri"
require 'json'
then in your controller or helper:
#set the uri
uri = URI.parse("http://my.site.com/uri")
#set the post params and get the respons
response = Net::HTTP.post_form(uri, {"first_param" => "my param", "second_param" => "another param"})
#get the json info
data = JSON.parse(response.body)
#set result to an ActiveRecord (maybe there is a better way to do this, I guess it depends on the response you get
#something = Mymodel.new
#something.name = data["name"]
...
#something.save
Hope it helps!
For GET request its:-
response = Typhoeus::Request.get("http://localhost:3000/users/1.json?oauth_token=12")
This returns Json response perfectly.
for Post request:-
response = Typhoeus::Request.post("http://localhost:3000/users/1.json?oauth_token=12",:params => {'[user][city]' => params[:location]})
is not working...
This is returning routing error.
Update:--
FOr login this api post call is working..
response = Typhoeus::Request.post(API_SERVER_ADDRESS + "user_sessions.json" + API_OAUTH_TOKEN, :params => {'[user_session][email]' => params[:email], '[user_session][password]' =>params[:password]})
In routes its
resources :users
and also web http request is working perfectly fine..
UPDATE
For example http request from rails log is:--
Parameters: {"commit"=>"Update", "authenticity_token"=>"8nvzCd0GF9IxjMcTfHOMJTPnycVPNIENMoMff8w4qAI=", "utf8"=>"✓", "id"=>"1", "user"=>{ "city"=>"abc"}}
Now i want to sent same kind of request..
The :params parameter should be a hash of your parms, meaning key-value pairs, so maybe something like this:
response = Typhoeus::Request.post("http://localhost:3000/users/1.json?oauth_token=12",:params => {:user => 'u', :city => 'c', :location => 'l'})
...or somesuch - whatever the parms are, whatever the values are. Your original doesn't translate into a meaningful hash for what you are wanting to do, I think.
Also, check your routing to make sure that what you are trying to do is properly routed.
Here is the solution
From this
response = Typhoeus::Request.put(API_SERVER_ADDRESS + "users/" +user_id + ".json" ,:params => {:oauth_token=>'12', :user=>{:city => params[:location]}})
Make sure you have declared a separate POST route in your routes.rb file. Even if the URLs are the same, different HTTP methods require different routes.
Using resources :users gives you the following by default:
GET /users/new # new
POST /users # create
GET /users/:id # show
GET /users/:id/edit # edit
PUT /users/:id # update
DELETE /users/:id # destroy
I plan to use JSON data in both request and response in my project and having some problems in testing.
After searching for a while, I find the following code which uses curl to post JSON data:
curl -H "Content-Type:application/json" -H "Accept:application/json" \
-d '{ "foo" : "bar" }' localhost:3000/api/new
In the controller I can access the JSON data simply using params[:foo] which is really easy. But for functional testing, I only find post and xhr (alias for xml_http_request).
How can I write functional test in rails to achieve the same effect as using curl? Or should I do test in other ways?
Here's what I've tried. I find the implementation for xhr in action_controller/test_case.rb, and tried to add jhr method simply changing 'Conetent-Type' and 'HTTP_ACCEPT'. (Added in test/test_helpers.rb.)
def json_http_request(request_method, action, parameters = nil, session = nil, flash = nil)
#request.env['Content-Type'] = 'Application/json'
#request.env['HTTP_ACCEPT'] ||= [Mime::JSON, Mime::JS, Mime::HTML, Mime::XML, 'text/xml', Mime::ALL].join(', ')
__send__(request_method, action, parameters, session, flash).tap do
#request.env.delete 'Content-Type'
#request.env.delete 'HTTP_ACCEPT'
end
end
alias jhr :json_http_request
I used this in the same way as xhr, but it does not work. I inspected the #response object and sees the body is " ".
I also find one similar question on Stack Overflow but it's for rails 2 and the answer for posting raw data does not work in rails 3.
As of Rails 5, the way to do this is:
post new_widget_url, as: :json, params: { foo: "bar" }
This will also set the Content-type header correctly (to application/json).
I found that this does exactly what I want – post JSON to a controller's action.
post :create, {:format => 'json', :user => { :email => "test#test.com", :password => "foobar"}}
Just specify appropriate content type:
post :index, '{"foo":"bar", "bool":true}', "CONTENT_TYPE" => 'application/json'
Json data should go as a string, not as a Hash.
Looking at stack trace running a test you can acquire more control on request preparation:
ActionDispatch::Integration::RequestHelpers.post => ActionDispatch::Integration::Session.process =>
Rack::Test::Session.env_for
Specifying :format does not work because request go as 'application/x-www-form-urlencoded' and json isn't parsed properly processing a request body.
Assuming you have a controller named api, a method named new, and you're in the test for the api controller:
#request.env["RAW_POST_DATA"] = '{ "foo" : "bar" }'
post :new
did the trick for me.
Here is a snippet that let me post json data to test my own app. rails 3
port = Rails.env.production? ? 80 : 3000
uri = URI.parse( Rails.application.routes.url_helpers.books_url(:host => request.host, :port => port, :format => :json) )
http = Net::HTTP.new(uri.host, uri.port)
request = Net::HTTP::Post.new(uri.request_uri)
request.content_type = 'application/json'
request.body = #json_data
response = http.request( request )
#result = response.body
Hope this helps others
As #taro suggests in a comment above, the syntax that works for me in functional and integration tests is:
post :create, {param1: 'value1', param2: 'value2', format: 'json'}
(The curly braces aren't always necessary, but sometimes it doesn't work if they're missing, so I always add them.)
Here's what params and request.format look like for a post of that sort:
params:
{"param1"=>"value1", "param2"=>"value2", "format"=>"json", "controller"=>"things", "action"=>"create"}
request.format:
application/json
The best answer I can come up with to this is you don't
Whether or not it was intentional it s maybe good that rails doesn't implement this for you.
In functional tests you really want to just test your controller and not rails method of deserialization or even that routing and mime detection are all setup correctly, those all fall under an IntegrationTest.
So for your controllers, don't pass JSON just pass your params hash like you normally would. Maybe adding :format as an argument as well if you need to check that and respond differently.
If you want to test the full stack move to an IntegrationTest