ruby-on-rails json api header rspec

ruby on rails - Usando Rspec, ¿cómo pruebo el formato JSON de mi controlador en Rails 3.0.11?



ruby-on-rails api (2)

Recorrí la web, pero, por desgracia, parece que no puedo hacer que Rspec envíe correctamente el tipo de contenido para que pueda probar mi API JSON. Estoy usando la gema RABL para plantillas, Rails 3.0.11 y Ruby 1.9.2-p180.

Mi salida de curl, que funciona bien (debería ser un 401, lo sé):

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."}

Muestra de uno de mis casos de prueba:

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 => { :name => "foo", :description => "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

Estas pruebas nunca pasan, siempre me redireccionan y mi tipo de contenido siempre es text/html , independientemente de cómo parezca especificar el tipo en mi bloque anterior:

# nope before do post :create, {}, { :format => :json } end # nada before do post :create, :format => Mime::JSON end # nuh uh before do request.env[''ACCEPT''] = ''application/json'' post :create, { :foo => :bar } end

Aquí está el resultado de rspec:

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 <top (required)>'' 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 ''<html><body>You are being <a href="http://test.host/">redirected</a>.</body></html>'' # ./spec/controllers/applications_controller_spec.rb:35:in `block (5 levels) in <top (required)>''

Como pueden ver, obtengo la redirección 302 para el formato HTML, aunque intento especificar ''application / json''.

Aquí está mi application_controller.rb , con el bit rescue_from:

class ApplicationController < ActionController::Base rescue_from ActiveRecord::RecordNotFound, :with => :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 => "error", :message => exception.message } format.html { redirect_to root_url } format.json { render :json => h, :status => :forbidden } format.xml { render :xml => h, :status => :forbidden } end end private def not_found(exception) respond_to do |format| h = { :status => "error", :message => exception.message } format.html { render :file => "#{RAILS_ROOT}/public/404.html", :status => :not_found } format.json { render :json => h, :status => :not_found } format.xml { render :xml => h, :status => :not_found } end end end

Y también applications_controller.rb , específicamente la acción ''crear'' que es lo que estoy tratando de probar. Es bastante feo en este momento porque estoy usando state_machine y anulando el método de eliminación.

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<<@environments unless @environments.blank? if params[:site_bindings] == "new" @site = Site.new(:name => params[:application][:name]) @environments.each do |e| @site.siteenvs << Siteenv.new(:environment_id => e) end end if @site @application.sites << @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 => @application) else render ''new'' end # super stinky :( @application.change_servers_on_appenvs(params[:servers]) unless params[:servers].blank? @application.save end

He visto el código fuente aquí: https://github.com/rails/rails/blob/master/actionpack/lib/action_controller/metal/responder.rb , y parece que debería responder correctamente, así como número de preguntas sobre desbordamiento de pila que parecen tener problemas similares y posibles soluciones, pero ninguna funciona para mí.

¿Qué estoy haciendo mal?


Intente mover la clave de :format dentro del hash de parámetros de la solicitud, así:

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 it "should not allow creation of an application" do params = { :format => ''json'', :application => { :name => "foo", :description => "bar" } } post :create, params Expect(Application.count).to eq(0) expect(response.status).to eq(403) expect(JSON.parse(response.body)["status"]).to eq("error") expect(JSON.parse(response.body)["message"]).to match(/authorized/) end end context "authorized" do end end end end

¡Déjame saber como va! esa es la forma en que he establecido mis pruebas, ¡y están funcionando bien!


Me doy cuenta de que la configuración :format => :json es una solución (como se señaló anteriormente). Sin embargo, quería probar las mismas condiciones que usarían los clientes de mi API. Mis clientes no establecerían el parámetro de :format en su lugar, establecerían el encabezado Accept HTTP. Si está interesado en esta solución, aquí está lo que utilicé:

# api/v1/test_controller_spec.rb require ''spec_helper.rb'' describe Api::V1::TestController do render_views context "when request sets accept => application/json" do it "should return successful response" do request.accept = "application/json" get :test response.should be_success end end end