2012-03-06 11 views
54

Przeszukałem sieć, ale, niestety, nie mogę sprawić, by Rspec poprawnie wysyłał typ zawartości, dzięki czemu mogę przetestować mój interfejs JSON API. Używam gem RABL dla szablonów, Rails 3.0.11 i Ruby 1.9.2-p180.Korzystając z Rspec, w jaki sposób przetestować format JSON mojego kontrolera w Railsach 3.0.11?

moje wyjście curl, który działa dobrze (powinno być 401, wiem):

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

próbki z jednego z moich testów:

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 

Testy te nie przechodzą choć, zawsze otrzymuję przekierowanie, a mój typ zawartości zawsze jest text/html, niezależnie od tego, jak wydaje mi się określony typ w moim poprzednim bloku:

# 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 

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

Jak widać Dostaję 302 przekierowanie do formatu HTML, choć staram się określić „application/json”.

Oto moja application_controller.rb, z bitem 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 

A także applications_controller.rb, konkretnie działanie „tworzyć”, który jest, co usiłuję przetestować. W tej chwili jest dość brzydka, ponieważ używam state_machine i nadpisuję metodę usuwania.

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 

Szukałem w kodzie źródłowym tutaj: https://github.com/rails/rails/blob/master/actionpack/lib/action_controller/metal/responder.rb i wydaje się, że powinien odpowiedzieć poprawnie, a także szereg pytań na przepełnienie stosu, które wydają się mieć podobne problemy i możliwe rozwiązania, ale żadna praca dla mnie.

Co robię źle?

Odpowiedz

56

Spróbuj przesunąć klawisz :format wewnątrz hash params wniosku, tak:

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 

Daj mi znać, jak to działa! to sposób, w jaki ustawiłem testy i działają dobrze!

+0

Dziękuję za skontaktowanie się ze mną Arturze. Wprowadziłem zmiany, o które prosiłeś, ale nadal uzyskuję takie same wyniki, gdzie test jest przekierowywany do HTML '1) ApplicationsController JSON tworzenie nowej aplikacji, gdy nie jest autoryzowany, nie powinno pozwalać na tworzenie aplikacji' 'Awaria/Błąd: JSON.parse (response.body) ["status"] == "błędy" ' ' JSON :: ParserError: ' ' 756: nieoczekiwany token w ' Jesteś redirected. '' '# ./spec/controllers/applications_controller_spec.rb: 25: w bloku (5 poziomów) w '' – gaahrdner

+0

Właśnie przejrzałem twój kod kontrolera! i zmieniłem trochę test! Wypróbuj to! –

+0

Niestety, to też nie zadziała, zgodnie z http://api.rubyonrails.org/classes/ActionController/TestCase/Behavior.html#method-i-post. Bez określania nagłówków 'Content-Type' lub' Accept', Railsy będą domyślnie miały postać 'text/html'. Podobne do tej instrukcji curl: 'curl -i -X ​​POST -d '{: application => {: name =>" foo ",: description =>" bar "}}' http: // localhost: 3000/applications' – gaahrdner

61

Zdaję sobie sprawę, że ustawienie :format => :json jest jednym z rozwiązań (jak wspomniano powyżej). Chciałem jednak przetestować te same warunki, których użyliby klienci do mojego API. Moi klienci nie ustawiliby parametru :format, zamiast tego ustawiliby nagłówek HTTP Accept. Jeśli interesuje Cię to rozwiązanie, oto, czego użyłem:

# 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 
+6

Po prostu dodanie 'request.accept =" application/json "' rozwiązało mój problem. To wydaje się być najprostszym rozwiązaniem. Dzięki! – user3233065

+0

Rails 4 i Rspec 3.2.1 to rozwiązało ten sam problem –

+0

Rails4 i Rspec3.3 również –

Powiązane problemy