rails matchers bot ruby-on-rails rspec ruby-on-rails-4.1 rspec3

ruby on rails - matchers - Establecer encabezado en la solicitud RSpec 3



ruby on rails selenium (5)

Estoy tratando de establecer el encabezado para algunas solicitudes RSpec que requieren autenticación. El encabezado es ACCESS_TOKEN . No importa cómo intente establecer el encabezado, nunca se establece. Sé que la aplicación funciona porque puedo probarla manualmente, simplemente no puedo hacer que funcionen las pruebas rspec. Vea el código fuente completo y las pruebas para este problema aquí: https://github.com/lightswitch05/rspec-set-header-example

Dado que la autenticación se utiliza en la mayoría de las especificaciones de mi solicitud, he creado un módulo de ayuda auxiliar para recuperar un token de acceso y configurarlo en el encabezado. A continuación se muestra un resumen de cómo intento configurar el encabezado, vea todo lo que he probado en la fuente completa

# my_app/spec/support/session_helper.rb module SessionHelper def retrieve_access_token post api_v1_session_path({email: ''[email protected]'', password: ''poor_password''}) expect(response.response_code).to eq 201 expect(response.body).to match(/"access_token":".{20}"/) parsed = JSON(response.body) token = parsed[''access_token''][''access_token''] @request.headers[''HTTP_ACCESS_TOKEN''] = token end end

una especificación de solicitud de ejemplo que usa este ayudante y debería funcionar, pero siempre falla porque el encabezado nunca se establece:

# my_app/spec/requests/posts_spec.rb # ... context "create" do it "creates a post" do retrieve_access_token post = FactoryGirl.build(:post) post api_v1_posts_path( post: { title: post.title, content: post.content } ) expect(response.body).to include(''"id":'') expect(response.body).to include(''"title":"'' + post.title + ''"'') expect(response.body).to include(''"content":"'' + post.content + ''"'') expect(response.response_code).to eq 201 end end

Sé que puedo configurar manualmente el encabezado en las solicitudes individuales de get y post , pero esa no es una solución mantenible para la autorización de toda la API. Imagina tener que cambiar todas las pruebas si el nombre del encabezado cambia ligeramente.


Apago la función que autentica la solicitud para devolver verdadero o cualquier valor devuelto por la función.

ApplicationController.any_instance.stub(:authenticate_request) { true }


El error común es tratar al controlador y solicitar pruebas por igual.

Sería bueno comenzar leyendo sobre las especificaciones del controlador y las especificaciones de solicitud . Como puede ver, las especificaciones del controlador simulan la solicitud de http, mientras que las especificaciones de la solicitud realizan la solicitud de pila completa.

Puede encontrar un buen artículo sobre por qué debería escribir especificaciones de controlador y qué probar here . Si bien es bueno escribirlos, en mi opinión no deberían estar tocando la base de datos.

Entonces, si bien la respuesta de Voxdei es parcialmente válida (después de cambiar las especificaciones de la solicitud a las especificaciones del controlador, su forma de configurar los encabezados funcionará), en mi opinión, se pierde el punto.

En las especificaciones de solicitud, no puede usar métodos de solicitud / controlador, debe pasar sus encabezados en hash como tercer argumento de sus métodos de solicitud, por lo que

post ''/something'', {}, {''MY-HEADER'' => ''value''}

Sin embargo, lo que podrías hacer es apagar la autenticación como:

before do allow(AccessToken).to receive("authenticate").and_return(true) end

Luego, puede probar su autenticación en una especificación para asegurarse de que funciona y usarla antes del filtro en otras especificaciones. Probablemente este enfoque sea mejor, ya que realizar una solicitud adicional cada vez que ejecute una especificación que requiera autenticación es una sobrecarga bastante grande.

También encontré una solicitud de extracción bastante interesante en la gema de la uva, que trata de agregar el comportamiento de los encabezados predeterminados, por lo que también podría probar este enfoque si realmente desea usar encabezados predeterminados en las especificaciones de la solicitud.


La respuesta de Surya es la mejor. Pero puedes secarlo un poco más:

def request_with_user_session(method, path, params={}, headers={}) headers.merge!(''HTTP_ACCESS_TOKEN'' => retrieve_access_token) send(method, path, params, headers) end

Aquí solo tiene un método y puede llamar al método de solicitud mediante el method parámetro dado.


Nota: esta respuesta se basa en lo que parece que está llamando api_v1_session_path con solicitud post a SessionsController para cada especificación que intenta ejecutar en las especificaciones de sus solicitudes.

Hay dos formas de resolver el problema que pensé que tienes aquí.

Solución 1 : cree otro método de ayuda en su SessionHelper o en algún otro archivo de ayuda llamado support / request_helper.rb (como prefiera). Me gustaría crear otro ayudante en support / request_helper.rb :

module RequestsHelper def get_with_token(path, params={}, headers={}) headers.merge!(''HTTP_ACCESS_TOKEN'' => retrieve_access_token) get path, params, headers end def post_with_token(path, params={}, headers={}) headers.merge!(''HTTP_ACCESS_TOKEN'' => retrieve_access_token) post path, params, headers end # similarly for xhr.. end

entonces en rails_helper.rb:

# Include the sessions helper config.include SessionHelper, type: :request # Include the requests helper config.include RequestsHelper, type: :request

cambiar session_helper.rb:

# my_app/spec/support/session_helper.rb module SessionHelper def retrieve_access_token post api_v1_session_path({email: ''[email protected]'', password: ''poor_password''}) expect(response.response_code).to eq 201 expect(response.body).to match(/"access_token":".{20}"/) parsed = JSON(response.body) parsed[''access_token''][''access_token''] # return token here!! end end

Ahora, puedes cambiar tus especificaciones de todas las solicitudes como esta:

describe Api::V1::PostsController do context "index" do it "retrieves the posts" do get_with_token api_v1_posts_path expect(response.body).to include(''"posts":[]'') expect(response.response_code).to eq 200 end it "requires a valid session key" do get api_v1_posts_path expect(response.body).to include(''"error":"unauthenticated"'') expect(response.response_code).to eq 401 end end end

Solución # 2 - Cambie las especificaciones / fábricas / access_token_factory.rb a:

FactoryGirl.define do factory :access_token do active true end # can be used when you want to test against expired access tokens: factory :inactive_access_token do active false end end

Ahora, cambie las especificaciones de todas sus solicitudes para usar access_token :

describe Api::V1::PostsController do context "index" do let(:access_token){ FactoryGirl.create(:access_token) } it "retrieves the posts" do # You will have to send HEADERS while making request like this: get api_v1_posts_path, nil, { ''HTTP_ACCESS_TOKEN'' => access_token.access_token } expect(response.body).to include(''"posts":[]'') expect(response.response_code).to eq 200 end it "requires a valid session key" do get api_v1_posts_path expect(response.body).to include(''"error":"unauthenticated"'') expect(response.response_code).to eq 401 end end end

Iría con " Solución # 1 ", ya que elimina la carga de hacer que recuerdes enviar HTTP_ACCESS_TOKEN en los encabezados cada vez que desees realizar dichas solicitudes.


Probablemente debido a cómo ahora Rspec trata los archivos de especificaciones. Ya no infiere automáticamente el tipo de especificación de una ubicación de archivo

Intente volver a configurar este comportamiento según lo que solía saber

RSpec.configure do |config| config.infer_spec_type_from_file_location! end

o configúrelo localmente para cada archivo de especificaciones del controlador en su proyecto

describe MyController, type: :controller do # your specs accessing @request end