Note that there are some explanatory texts on larger screens.

plurals
  1. POUsing Rspec, how do I test the JSON format of my controller in Rails 3.0.11?
    primarykey
    data
    text
    <p>I've scoured the web, but, alas, I just can't seem to get Rspec to correctly send content-type so I can test my JSON API. I'm using the RABL gem for templates, Rails 3.0.11, and Ruby 1.9.2-p180.</p> <p>My curl output, which works fine (should be a 401, I know):</p> <pre><code>mrsnuggles:tmp gaahrdner$ curl -i -H "Accept: application/json" -X POST -d @bleh http://localhost:3000/applications HTTP/1.1 403 Forbidden Content-Type: application/json; charset=utf-8 Cache-Control: no-cache X-Ua-Compatible: IE=Edge X-Runtime: 0.561638 Server: WEBrick/1.3.1 (Ruby/1.9.2/2011-02-18) Date: Tue, 06 Mar 2012 01:10:51 GMT Content-Length: 74 Connection: Keep-Alive Set-Cookie: _session_id=8e8b73b5a6e5c95447aab13dafd59993; path=/; HttpOnly {"status":"error","message":"You are not authorized to access this page."} </code></pre> <p>Sample from one of my test cases:</p> <pre><code>describe ApplicationsController do render_views disconnect_sunspot let(:application) { Factory.create(:application) } subject { application } context "JSON" do describe "creating a new application" do context "when not authorized" do before do json = { :application =&gt; { :name =&gt; "foo", :description =&gt; "bar" } } request.env['CONTENT_TYPE'] = 'application/json' request.env['RAW_POST_DATA'] = json post :create end it "should not allow creation of an application" do Application.count.should == 0 end it "should respond with a 403" do response.status.should eq(403) end it "should have a status and message key in the hash" do JSON.parse(response.body)["status"] == "error" JSON.parse(response.body)["message"] =~ /authorized/ end end context "authorized" do end end end end </code></pre> <p>These tests never pass though, I always get redirected and my content-type is always <code>text/html</code>, regardless of how I seem to specify the type in my before block:</p> <pre><code># nope before do post :create, {}, { :format =&gt; :json } end # nada before do post :create, :format =&gt; Mime::JSON end # nuh uh before do request.env['ACCEPT'] = 'application/json' post :create, { :foo =&gt; :bar } end </code></pre> <p>Here is the rspec output:</p> <pre><code>Failures: 1) ApplicationsController JSON creating a new application when not authorized should respond with a 403 Failure/Error: response.status.should eq(403) expected 403 got 302 (compared using ==) # ./spec/controllers/applications_controller_spec.rb:31:in `block (5 levels) in &lt;top (required)&gt;' 2) ApplicationsController JSON creating a new application when not authorized should have a status and message key in the hash Failure/Error: JSON.parse(response.body)["status"] == "errors" JSON::ParserError: 756: unexpected token at '&lt;html&gt;&lt;body&gt;You are being &lt;a href="http://test.host/"&gt;redirected&lt;/a&gt;.&lt;/body&gt;&lt;/html&gt;' # ./spec/controllers/applications_controller_spec.rb:35:in `block (5 levels) in &lt;top (required)&gt;' </code></pre> <p>As you can see I'm getting the 302 redirect for the HTML format, even though I'm trying to specify 'application/json'.</p> <p>Here is my <code>application_controller.rb</code>, with the rescue_from bit:</p> <pre><code>class ApplicationController &lt; ActionController::Base rescue_from ActiveRecord::RecordNotFound, :with =&gt; :not_found protect_from_forgery helper_method :current_user helper_method :remove_dns_record rescue_from CanCan::AccessDenied do |exception| flash[:alert] = exception.message respond_to do |format| h = { :status =&gt; "error", :message =&gt; exception.message } format.html { redirect_to root_url } format.json { render :json =&gt; h, :status =&gt; :forbidden } format.xml { render :xml =&gt; h, :status =&gt; :forbidden } end end private def not_found(exception) respond_to do |format| h = { :status =&gt; "error", :message =&gt; exception.message } format.html { render :file =&gt; "#{RAILS_ROOT}/public/404.html", :status =&gt; :not_found } format.json { render :json =&gt; h, :status =&gt; :not_found } format.xml { render :xml =&gt; h, :status =&gt; :not_found } end end end </code></pre> <p>And also <code>applications_controller.rb</code>, specifically the 'create' action which is what I'm trying to test. It's fairly ugly at the moment because I'm using <code>state_machine</code> and overriding the delete method.</p> <pre><code> def create # this needs to be cleaned up and use accepts_attributes_for @application = Application.new(params[:application]) @environments = params[:application][:environment_ids] @application.environment_ids&lt;&lt;@environments unless @environments.blank? if params[:site_bindings] == "new" @site = Site.new(:name =&gt; params[:application][:name]) @environments.each do |e| @site.siteenvs &lt;&lt; Siteenv.new(:environment_id =&gt; e) end end if @site @application.sites &lt;&lt; @site end if @application.save if @site @site.siteenvs.each do |se| appenv = @application.appenvs.select {|e| e.environment_id == se.environment_id } se.appenv = appenv.first se.save end end flash[:success] = "New application created." respond_with(@application, :location =&gt; @application) else render 'new' end # super stinky :( @application.change_servers_on_appenvs(params[:servers]) unless params[:servers].blank? @application.save end </code></pre> <p>I've looked at the source code here: <a href="https://github.com/rails/rails/blob/master/actionpack/lib/action_controller/metal/responder.rb" rel="noreferrer">https://github.com/rails/rails/blob/master/actionpack/lib/action_controller/metal/responder.rb</a>, and it seems it should respond correctly, as well as a number of questions on stack overflow that seem to have similar issues and possible solutions, but none work for me.</p> <p>What am I doing wrong?</p>
    singulars
    1. This table or related slice is empty.
    1. This table or related slice is empty.
    plurals
    1. This table or related slice is empty.
    1. This table or related slice is empty.
    1. This table or related slice is empty.
    1. This table or related slice is empty.
 

Querying!

 
Guidance

SQuiL has stopped working due to an internal error.

If you are curious you may find further information in the browser console, which is accessible through the devtools (F12).

Reload