À l'aide de Rspec, comment puis-je tester le format JSON de mon contrôleur dans les Rails 3.0.11?

J'ai écumé le web, mais, hélas, je ne peux pas sembler obtenir Rspec pour envoyer correctement le type de contenu de sorte que je puisse tester mon API JSON. Je suis à l'aide de la RABL gem pour les modèles de Rails 3.0.11, et Ruby 1.9.2-p180.

Mon curl sortie, qui fonctionne très bien (ce qui devrait être un 401, je sais):

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

De l'échantillon à partir de l'un de mes cas de test:

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

Ces tests ne passent jamais bien, j'ai toujours été redirigé et mon type de contenu est toujours text/html, indépendamment de la façon dont je semble spécifier le type dans mon avant de bloc:

# 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

Ici est la rspec de sortie:

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)>'

Comme vous pouvez le voir je suis de la redirection 302 pour le format HTML, même si je suis d'essayer de préciser, "application/json".

Voici mon application_controller.rb, avec la rescue_from bits:

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

Et aussi applications_controller.rb, plus précisément la "création" de l'action qui est ce que je suis en train de tester. C'est assez moche pour le moment car je suis en utilisant state_machine et en remplaçant la méthode delete.

  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

J'ai regardé le code source ici: https://github.com/rails/rails/blob/master/actionpack/lib/action_controller/metal/responder.rb, et il semble qu'il doit répondre correctement, ainsi que d'un certain nombre de questions sur stack overflow qui semblent avoir les mêmes problèmes et les solutions possibles, mais aucun travail pour moi.

Ce que je fais mal?

InformationsquelleAutor gaahrdner | 2012-03-06