使用Rspec,我如何在Rails 3.0.11中testing我的控制器的JSON格式?

我search了网页,但是,唉,我似乎无法让Rspec正确发送内容types,所以我可以testing我的JSON API。 我正在使用RABL gem模板,Rails 3.0.11和Ruby 1.9.2-p180。

我的curl输出,工作正常(应该是一个401,我知道):

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

来自我的一个testing用例的样本:

 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 

这些testing永远不会通过,我总是得到redirect,我的内容types始终是text/html ,无论我似乎在我之前的块指定types:

 # 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 

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

正如你所看到的,我得到了302格式的HTMLredirect,即使我正在试图指定'application / json'。

这是我的application_controller.rb ,与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 

还有applications_controller.rb ,特别是我正在testing的“创build”动作。 这是相当丑陋的,因为我正在使用state_machine并重写删除方法。

  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 

我已经看了这里的源代码: https : //github.com/rails/rails/blob/master/actionpack/lib/action_controller/metal/responder.rb ,它似乎应该正确回应,以及堆栈溢出问题似乎有类似的问题和可能的解决scheme,但没有为我工作。

我究竟做错了什么?

尝试移动“it”块内的post,如下所示:

 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 json = { :format => 'json', :application => { :name => "foo", :description => "bar" } } post :create, json Application.count.should == 0 response.status.should eq(403) JSON.parse(response.body)["status"] == "error" JSON.parse(response.body)["message"] =~ /authorized/ end end context "authorized" do end end end end 

让我知道事情的后续! 多数民众赞成在我已经设置了我的testing的方式,他们工作得很好!

我意识到设置:format => :json是一个解决scheme(如上所述)。 不过,我想testing的客户端到我的API将使用相同的条件。 我的客户端不会设置:format参数,而是设置Accept HTTP标头。 如果你对这个解决scheme感兴趣,下面是我使用的:

 # 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