tutorial rails matchers examples bot ruby-on-rails ruby rspec rspec-rails actiondispatch

ruby-on-rails - rails - rspec tutorial



Pruebe el código de estado HTTP en algunos ejemplos de solicitud de rieles RSpec, pero para la excepción en otros (5)

En una aplicación Rails 4.2.0 probada con rspec-rails , proporciono una API web JSON con un recurso similar a REST con un atributo obligatorio mand_attr .

Me gustaría probar que esta API responde con el código HTTP 400 ( BAD REQUEST ) cuando falta ese atributo en una solicitud POST. (Vea el segundo ejemplo.) Mi controlador intenta causar este código HTTP al lanzar un ActionController::ParameterMissing , como se ilustra en el primer ejemplo de RSpec a continuación.

En otros ejemplos de RSpec, quiero que las excepciones generadas sean rescatadas por los ejemplos (si se esperan) o que lleguen al corredor de prueba, para que se muestren al desarrollador (si el error es inesperado), por lo tanto no deseo para eliminar

# Raise exceptions instead of rendering exception templates. config.action_dispatch.show_exceptions = false

desde config/environments/test.rb

Mi plan era tener algo como lo siguiente en una especificación de solicitud :

describe ''POST'' do let(:perform_request) { post ''/my/api/my_ressource'', request_body, request_header } let(:request_header) { { ''CONTENT_TYPE'' => ''application/json'' } } context ''without mandatory attribute'' do let(:request_body) do {}.to_json end it ''raises a ParameterMissing error'' do expect { perform_request }.to raise_error ActionController::ParameterMissing, ''param is missing or the value is empty: mand_attr'' end context ''in production'' do ############################################################### # How do I make this work without breaking the example above? # ############################################################### it ''reports BAD REQUEST (HTTP status 400)'' do perform_request expect(response).to be_a_bad_request # Above matcher provided by api-matchers. Expectation equivalent to # expect(response.status).to eq 400 end end end # Below are the examples for the happy path. # They''re not relevant to this question, but I thought # I''d let you see them for context and illustration. context ''with mandatory attribute'' do let(:request_body) do { mand_attr: ''something'' }.to_json end it ''creates a ressource entry'' do expect { perform_request }.to change(MyRessource, :count).by 1 end it ''reports that a ressource entry was created (HTTP status 201)'' do perform_request expect(response).to create_resource # Above matcher provided by api-matchers. Expectation equivalent to # expect(response.status).to eq 201 end end end

He encontrado dos soluciones de trabajo y una de trabajo parcial que publicaré como respuestas. Pero no estoy particularmente contento con ninguno de ellos, así que si puedes encontrar algo mejor (o simplemente diferente), ¡me gustaría ver tu enfoque! Además, si una especificación de solicitud es del tipo incorrecto para probar esto, me gustaría saberlo.

Preveo la pregunta

¿Por qué está probando el marco de Rails en lugar de solo su aplicación Rails? ¡El framework Rails tiene sus propias pruebas!

así que permítame responderlo preventivamente: siento que no estoy probando el marco en sí mismo aquí, pero si estoy usando el marco correctamente. Mi controlador no hereda de ActionController::Base sino de ActionController::API y no sabía si ActionController::API usa ActionDispatch::ExceptionWrapper de manera predeterminada o si primero habría tenido que decirle a mi controlador que lo hiciera de alguna manera .


Devolviendo nil desde una configuración de aplicación parcialmente simulada con

context ''in production'' do before do allow(Rails.application.config.action_dispatch).to receive(:show_exceptions) end it ''reports BAD REQUEST (HTTP status 400)'' do perform_request expect(response).to be_a_bad_request end end

o más explícitamente con

context ''in production'' do before do allow(Rails.application.config.action_dispatch).to receive(:show_exceptions).and_return nil end it ''reports BAD REQUEST (HTTP status 400)'' do perform_request expect(response).to be_a_bad_request end end

Funcionaría si ese fuera el único ejemplo que se está ejecutando. Pero si lo fuera, podríamos descartar la configuración de config/environments/test.rb , por lo que es un poco discutible. Cuando hay varios ejemplos, esto no funcionará , ya que Rails.application.env_config() , que consulta esta configuración, almacena en caché su resultado.


En mi opinión, la prueba de excepción no pertenece a una especificación de solicitud; las especificaciones de solicitud generalmente son para probar su api desde la perspectiva del cliente para asegurarse de que toda la pila de aplicaciones funciona como se espera. También son de naturaleza similar a las pruebas de características cuando se prueba una interfaz de usuario. Entonces, debido a que sus clientes no verán esta excepción, es probable que no pertenezca allí.

También puedo simpatizar con su preocupación acerca de usar el marco correctamente y querer asegurarme de eso, pero parece que se está involucrando demasiado con el funcionamiento interno del marco.

Lo que haría sería primero averiguar si estoy usando la función en el marco correctamente, (esto se puede hacer con un enfoque TDD o como un pico); una vez que comprendo cómo lograr lo que quiero lograr, escribiría una especificación de solicitud en la que asumo el rol de cliente y no me preocupo por los detalles del marco; sólo prueba la salida dada entradas específicas.

Me interesaría ver el código que ha escrito en su controlador porque también se puede usar para determinar la estrategia de prueba. Si escribió el código que genera la excepción, entonces eso podría justificar una prueba, pero lo ideal sería que fuera una prueba unitaria para el controlador. Lo que sería una prueba de controlador en un rspec-rails .


Usted querría usar filtros RSpec para eso. Si lo haces de esta manera, la modificación a Rails.application.config.action_dispatch.show_exceptions será local al ejemplo y no interferirá con tus otras pruebas:

# This configure block can be moved into a spec helper RSpec.configure do |config| config.before(:example, exceptions: :catch) do allow(Rails.application.config.action_dispatch).to receive(:show_exceptions) { true } end end RSpec.describe ''POST'' do let(:perform_request) { post ''/my/api/my_ressource'', request_body } context ''without mandatory attribute'' do let(:request_body) do {}.to_json end it ''raises a ParameterMissing error'' do expect { perform_request }.to raise_error ActionController::ParameterMissing end context ''in production'', exceptions: :catch do it ''reports BAD REQUEST (HTTP status 400)'' do perform_request expect(response).to be_a_bad_request end end end end

Las exceptions: :catch es "metadatos arbitrarios" en el lenguaje RSpec, elegí el nombre aquí para facilitar la lectura.


Rails.application.env_config() para devolver un resultado modificado

context ''in production'' do before do # We don''t really want to test in a production environment, # just in a slightly deviating test environment, # so use the current test environment as a starting point ... pseudo_production_config = Rails.application.env_config.clone # ... and just remove the one test-specific setting we don''t want here: pseudo_production_config.delete ''action_dispatch.show_exceptions'' # Then let `Rails.application.env_config()` return that modified Hash # for subsequent calls within this RSpec context. allow(Rails.application).to receive(:env_config). and_return pseudo_production_config end it ''reports BAD REQUEST (HTTP status 400)'' do perform_request expect(response).to be_a_bad_request end end

Hará el truco. Tenga en cuenta que env_config() el resultado de env_config() , para que no modifiquemos el hash original que afectaría a todos los ejemplos.


context ''in production'' do around do |example| # Run examples without the setting: show_exceptions = Rails.application.env_config.delete ''action_dispatch.show_exceptions'' example.run # Restore the setting: Rails.application.env_config[''action_dispatch.show_exceptions''] = show_exceptions end it ''reports BAD REQUEST (HTTP status 400)'' do perform_request expect(response).to be_a_bad_request end end

Hará el truco, pero se siente un poco sucio. Funciona porque Rails.application.env_config() da acceso al Hash subyacente que usa para almacenar en caché su resultado, por lo que podemos modificarlo directamente.