Pruebas de Creación y Actualización con Autenticación en Rails

Clase 25 de 33Curso de Creación de APIs con Ruby on Rails

Resumen

La autenticación bien probada evita regresiones y errores silenciosos. Aquí se valida, paso a paso, cómo asegurar en Rails que solo usuarios autenticados puedan crear y actualizar, cómo responder con códigos correctos y cómo refactorizar la autenticación en un concern reutilizable. El foco está en pruebas claras, estados HTTP precisos y manejo explícito de excepciones.

¿Cómo validar la creación con autenticación en Rails?

Para creación se definen dos contextos: con autenticación válida y sin autenticación. Se envían parámetros anidados bajo la clave post, tal como exige el controlador en su método de parámetros fuertes. La verificación cubre el contenido del payload y el código de estado.

  • Con autenticación: se debe poder crear y responder con created.
  • Sin autenticación: no debe crear y debe responder unauthorized.
  • Los parámetros de creación incluyen: title, content y published.
  • El payload esperado incluye: id, title, content, published y author.

¿Qué parámetros y headers usar en el request?

  • Parám: enviar bajo la clave post.
  • Headers: usar auth headers válidos.
  • Método: POST a la ruta de posts.
# RSpec
context 'with valid auth' do
  let(:create_params) do
    { post: { title: 'title', content: '...', published: true } }
  end

  it 'crea el post y responde created' do
    post '/posts', params: create_params, headers: auth_headers
    expect(payload).to include('id', 'title', 'content', 'published', 'author')
    expect(response).to have_http_status(:created)
  end
end

context 'without authentication' do
  it 'rechaza la creación con unauthorized' do
    post '/posts', params: { post: { title: 'title', content: '...', published: true } }
    expect(payload).to include('error')
    expect(response).to have_http_status(:unauthorized)
  end
end

¿Qué validar en el payload y response?

  • Contenido: incluir claves id, title, content, published y author.
  • Errores: incluir error si falta autenticación.
  • Estados: usar created y unauthorized según el caso.

¿Qué pruebas aseguran la actualización y control de acceso?

La actualización replica la estructura de creación, con énfasis en control de acceso. Con autenticación se puede actualizar el propio post; intentar actualizar el post de otro usuario debe responder not found. El estado esperado para éxito es ok.

  • Caso propio: actualizar vía PUT el recurso del usuario autenticado.
  • Caso ajeno: no permitir y responder not found.
  • Sin autenticación: ya cubierto por las validaciones de creación, evitar redundancia.

¿Cómo probar el update del propio post?

  • Ruta: PUT a /posts/:id del usuario autenticado.
  • Parámetros: usar update params con la misma estructura.
  • Validaciones: coincidir id y responder ok.
context "when updating user's post" do
  let(:update_params) do
    { post: { title: 'new title', content: '...', published: true } }
  end

  it 'actualiza y responde ok' do
    put "/posts/#{user_post.id}", params: update_params, headers: auth_headers
    expect(payload).to include('id', 'title', 'content', 'published', 'author')
    expect(payload['id']).to eq(user_post.id)
    expect(response).to have_http_status(:ok)
  end
end

¿Cómo bloquear el update de otro usuario?

  • Ruta: PUT a /posts/:id de un tercero.
  • Resultado: payload con error y estado not found.
context "when updating other user's post" do
  let(:update_params) do
    { post: { title: 'any', content: '...', published: true } }
  end

  it 'rechaza con not found' do
    put "/posts/#{other_user_post.id}", params: update_params, headers: auth_headers
    expect(payload).to include('error')
    expect(response).to have_http_status(:not_found)
  end
end

¿Cómo manejar excepciones y reutilizar autenticación con concerns?

Al buscar posts con find, si no existe el registro, Rails lanza ActiveRecord::RecordNotFound. Sin manejarla, un capturador genérico devuelve 500. Se debe capturar explícitamente y responder 404 not found. Además, para evitar duplicación, el método de autenticación se mueve a un concern llamado Secured y se incluye en los controladores que lo requieran.

  • Excepción: ActiveRecord::RecordNotFound debe mapear a not found.
  • Reutilización: extraer authenticate_user a un module compartido.
  • Limpieza: eliminar pruebas antiguas de creación y actualización sin autenticación, mantener listado y lectura de publicados.

¿Cómo capturar ActiveRecord::RecordNotFound y responder 404?

# ApplicationController
rescue_from ActiveRecord::RecordNotFound do
  render json: { error: 'Not found' }, status: :not_found
end

¿Cómo extraer authenticate_user a un concern reutilizable?

# app/controllers/concerns/secured.rb
module Secured
  def authenticate_user
    # implementación movida desde el controlador.
  end
end

# posts_controller.rb
class PostsController < ApplicationController
  include Secured
  # acciones
end

¿Te gustaría compartir cómo estructuras tus contexts y expectativas para mantener las pruebas legibles y confiables?

      Pruebas de Creación y Actualización con Autenticación en Rails