ruby on rails - ¿Cómo verificar una respuesta JSON utilizando RSpec?
ruby-on-rails (10)
Tengo el siguiente código en mi controlador:
format.json { render :json => {
:flashcard => @flashcard,
:lesson => @lesson,
:success => true
}
En mi prueba de controlador RSpec, quiero verificar que cierto escenario recibe una respuesta JSON exitosa, así que tenía la siguiente línea:
controller.should_receive(:render).with(hash_including(:success => true))
Aunque cuando ejecuto mis pruebas obtengo el siguiente error:
Failure/Error: controller.should_receive(:render).with(hash_including(:success => false))
(#<AnnoController:0x00000002de0560>).render(hash_including(:success=>false))
expected: 1 time
received: 0 times
¿Estoy verificando la respuesta incorrectamente?
Al usar Rails 5 (actualmente todavía en versión beta), hay un nuevo método, parsed_body
en la respuesta de prueba, que devolverá la respuesta analizada como en qué fue codificada la última solicitud.
La confirmación en GitHub: https://github.com/rails/rails/commit/eee3534b
Aprovechando la respuesta de Kevin Trowbridge
response.header[''Content-Type''].should include ''application/json''
Encontré un marcador de clientes aquí: https://raw.github.com/gist/917903/92d7101f643e07896659f84609c117c4c279dfad/have_content_type.rb
Ponlo en spec / support / matchers / have_content_type.rb y asegúrate de cargar cosas del soporte con algo como esto en tu spec / spec_helper.rb
Dir[Rails.root.join(''spec/support/**/*.rb'')].each {|f| require f}
Aquí está el código en sí, en caso de que desaparezca del enlace dado.
RSpec::Matchers.define :have_content_type do |content_type|
CONTENT_HEADER_MATCHER = /^(.*?)(?:; charset=(.*))?$/
chain :with_charset do |charset|
@charset = charset
end
match do |response|
_, content, charset = *content_type_header.match(CONTENT_HEADER_MATCHER).to_a
if @charset
@charset == charset && content == content_type
else
content == content_type
end
end
failure_message_for_should do |response|
if @charset
"Content type #{content_type_header.inspect} should match #{content_type.inspect} with charset #{@charset}"
else
"Content type #{content_type_header.inspect} should match #{content_type.inspect}"
end
end
failure_message_for_should_not do |model|
if @charset
"Content type #{content_type_header.inspect} should not match #{content_type.inspect} with charset #{@charset}"
else
"Content type #{content_type_header.inspect} should not match #{content_type.inspect}"
end
end
def content_type_header
response.headers[''Content-Type'']
end
end
Otro enfoque para probar solo una respuesta JSON (no que el contenido dentro contenga un valor esperado) es analizar la respuesta usando ActiveSupport:
ActiveSupport::JSON.decode(response.body).should_not be_nil
Si la respuesta no es analizable JSON, se lanzará una excepción y la prueba fallará.
Podría analizar el cuerpo de respuesta de esta manera:
parsed_body = JSON.parse(response.body)
Entonces puedes hacer tus afirmaciones en contra de ese contenido analizado.
parsed_body["foo"].should == "bar"
Podrías mirar en el ''Content-Type''
para ver si está correcto?
response.header[''Content-Type''].should include ''text/javascript''
Puede examinar el objeto de respuesta y verificar que contenga el valor esperado:
@expected = {
:flashcard => @flashcard,
:lesson => @lesson,
:success => true
}.to_json
get :action # replace with action name / params as necessary
response.body.should == @expected
EDITAR
Cambiar esto a una post
hace un poco más complicado. Aquí hay una manera de manejarlo:
it "responds with JSON" do
my_model = stub_model(MyModel,:save=>true)
MyModel.stub(:new).with({''these'' => ''params''}) { my_model }
post :create, :my_model => {''these'' => ''params''}, :format => :json
response.body.should == my_model.to_json
end
Tenga en cuenta que mock_model
no responderá a to_json
, por lo que se stub_model
o una instancia de modelo real.
Simple y fácil de hacer esto.
# set some variable on success like :success => true in your controller
controller.rb
render :json => {:success => true, :data => data} # on success
spec_controller.rb
parse_json = JSON(response.body)
parse_json["success"].should == true
También está la gema json_spec , que vale la pena mirar
También puede definir una función auxiliar dentro de la spec/support/
module ApiHelpers
def json_body
JSON.parse(response.body)
end
end
RSpec.configure do |config|
config.include ApiHelpers, type: :request
end
y use json_body
cada vez que necesite acceder a la respuesta JSON.
Por ejemplo, dentro de su especificación de solicitud puede usarlo directamente
context ''when the request contains an authentication header'' do
it ''should return the user info'' do
user = create(:user)
get URL, headers: authenticated_header(user)
expect(response).to have_http_status(:ok)
expect(response.content_type).to eq(''application/vnd.api+json'')
expect(json_body["data"]["attributes"]["email"]).to eq(user.email)
expect(json_body["data"]["attributes"]["name"]).to eq(user.name)
end
end