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?
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) wWłaśnie przejrzałem twój kod kontrolera! i zmieniłem trochę test! Wypróbuj to! –
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