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