2010-01-20 11 views
49

Busco para enviar datos de correos primas (por ejemplo unparamaterized JSON) a uno de mis reguladores para la prueba:¿Cómo se envían los datos de entrada sin procesar en una prueba funcional de Rails?

class LegacyOrderUpdateControllerTest < ActionController::TestCase 
    test "sending json" do 
    post :index, '{"foo":"bar", "bool":true}' 
    end 
end 

pero esto me da un error de NoMethodError: undefined method `symbolize_keys' for #<String:0x00000102cb6080>.

¿Cuál es la forma correcta de enviar datos sin procesar de la publicación en ActionController::TestCase?

Aquí hay un código de controlador:

def index 
    post_data = request.body.read 
    req = JSON.parse(post_data) 
end 
+0

Tengo curiosidad de cómo hacer esto, también, para probar una API basada en JSON. – tadman

Respuesta

55

Me encontré con el mismo problema hoy y encontré una solución.

En su test_helper.rb definir el siguiente método en el interior de ActiveSupport::TestCase:

def raw_post(action, params, body) 
    @request.env['RAW_POST_DATA'] = body 
    response = post(action, params) 
    @request.env.delete('RAW_POST_DATA') 
    response 
end 

En su prueba de funcionamiento, al igual que lo utilizan el método post pero pasar el cuerpo de la entrada en bruto como el tercer argumento.

class LegacyOrderUpdateControllerTest < ActionController::TestCase 
    test "sending json" do 
    raw_post :index, {}, {:foo => "bar", :bool => true}.to_json 
    end 
end 

He probado esto en los carriles 2.3.4 al leer el cuerpo de la entrada en bruto usando

request.raw_post 

en lugar de

request.body.read 

Si nos fijamos en la source code verá que raw_post simplemente envuelve request.body.read con un cheque para este RAW_POST_DATA en request env hash.

+0

You rock, gracias – brian

+1

Este enfoque sigue funcionando correctamente en Rails 3.1 – cfeduke

+1

Huh, yeah and carriles 3.2 también. ¡Gracias! – zigomir

-5
post :index, {:foo=> 'bar', :bool => 'true'} 
+0

Eso no es JSON en bruto, esa es la interpretación hash de eso. – tadman

0

El método post espera un hash de pares de nombre-valor, por lo que tendrá que hacer algo como esto:

post :index, :data => '{"foo":"bar", "bool":true}' 

Luego, en su controlador, obtenga los datos para ser analizados así:

post_data = params[:data] 
+0

He intentado esto, debe ser completamente cruda, aunque {"response": "error", "errors": "no se puede analizar la solicitud: 598: token inesperado en 'data = – brian

+0

¿Cómo se analiza el JSON en su controlador? ¿Podría agregar algún código de controlador a su pregunta? –

+0

bien, lo haré ahora – brian

15

De hecho, resolví los mismos problemas simplemente agregando una línea antes de simular la solicitud de rspec. Lo que haga es llenar el "RAW_POST_DATA". Traté de eliminar los atributos var en la publicación: crear, pero si lo hago, no encuentra la acción.

Aquí mi solución.

 
def do_create(attributes) 
    request.env['RAW_POST_DATA'] = attributes.to_json 
    post :create, attributes 
end 

En el controlador el código que necesita para leer el JSON es algo similar a esto

 
    @property = Property.new(JSON.parse(request.body.read)) 
+1

¡genial! Solo una línea, y la tuve funcionando incluso sin los 'atributos' enviados para publicar. –

+0

esto funcionó para Rails 3.2.x, pero parece un hack-ish. RSpec debería admitir atributos que se publican como JSON. Rspec error? – Tilo

9

En cuanto a seguimiento de la pila de realizar una prueba puede adquirir un mayor control sobre la preparación de pedidos: ActionDispatch: : Integración :: RequestHelpers.post =>ActionDispatch::Integration::Session.process => Rack::Test::Session.env_for

Puede pasar cadena JSON como: params y especificar un tipo de contenido "application/json". En otro caso, el tipo de contenido se establecerá en "application/x-www-form-urlencoded" y su json se analizará correctamente.

Así que todo lo que necesita es especificar "CONTENT_TYPE":

post :index, '{"foo":"bar", "bool":true}', "CONTENT_TYPE" => 'application/json' 
+5

Esto no funciona para mí. Aparece un error como "método no definido' symbolize_keys "para # " –

+0

Sí, puedes echar un vistazo a la fuente de Rails para 'publicar' (que llama' proceso'). Si el primer arg después de la acción es un String, se leerá como 'RAW_POST_DATA'. –

+0

Esto funciona en Rails 4+, no en algunas versiones anteriores. –

4

Si está utilizando RSpec (> = 2.12.0) y la escritura Solicitar especificaciones, el módulo que se incluye es ActionDispatch::Integration::Runner. Si echa un vistazo al código fuente, puede observar que el método post llama al process, que acepta un parámetro rack_env.

Todo esto significa que sólo tiene que hacer lo siguiente en su especificación:

#spec/requests/articles_spec.rb 

post '/articles', {}, {'RAW_POST_DATA' => 'something'} 

Y en el controlador:

#app/controllers/articles_controller.rb 

def create 
    puts request.body.read 
end 
+1

Esto solo funciona para pruebas de integración y no para pruebas funcionales extendidas desde ActionController :: TestCase. – bibstha

1

El uso de los carriles 4, que estaba buscando para hacer esto para probar la procesamiento de xml sin formato que se estaba publicando en el controlador. Yo era capaz de hacerlo solo con el cordón a la mensaje:

raw_xml = File.read("my_raw.xml") 
post :message, raw_xml, format: :xml 

Creo que si el parámetro proporcionado es una cadena, sólo se transfieren a las del controlador como el cuerpo.

+0

No sé por qué está siendo rechazado, esto es correcto. El segundo argumento para 'publicar' puede ser un String o IO, y eso se convierte en el cuerpo. – ZiggyTheHamster

0

A partir de los carriles 4.1.5, este fue el único que trabajó para mí:

class LegacyOrderUpdateControllerTest < ActionController::TestCase 
    def setup 
    @request.headers["Content-Type"] = 'application/json' 
    end 

    test "sending json" do 
    post :index, '{"foo":"bar", "bool":true}'.to_json, { account_id: 5, order_id: 10 } 
    end 
end 

para una dirección URL en/cuentas/5/órdenes/10/artículos. Esto obtiene los parámetros de URL transmitidos, así como el cuerpo JSON. Por supuesto, si las órdenes no están incrustadas, entonces puedes dejar el hash de params.

class LegacyOrderUpdateControllerTest < ActionController::TestCase 
    def setup 
    @request.headers["Content-Type"] = 'application/json' 
    end 

    test "sending json" do 
    post :index, '{"foo":"bar", "bool":true}'.to_json 
    end 
end 
+0

String # to_json significa que está pasando una cadena JSON que contiene JSON dentro de ella ... eso no es lo que usted quiere. – ZiggyTheHamster

2

versión de Rails 5:

post :create, body: '{"foo": "bar", "bool": true}' 

Ver here - body parámetro cadena se trata como cuerpo de la petición prima.

2

para aquellos que utilizan pruebas de integración Rails5 +, la forma (no documentado) de hacer esto es pasar una cadena en el argumento params, por lo que:

post '/path', params: raw_body, headers: { 'Content-Type' => 'application/json' } 
+1

Esto dejó de funcionar para mí en los carriles 5.1. Si mira request.body.read, está vacío. –

0

En los carriles, 5,1 el siguiente trabajo para mí cuando se hace una eliminar petición que necesitan los datos en el cuerpo:

delete your_app_url, as: :json, env: { 
    "RAW_POST_DATA" => {"a_key" => "a_value"}.to_json 
} 

NOTA: Esto sólo funciona cuando se hace una prueba de integración.

+0

Eliminar era un error en la biblioteca subyacente. https://github.com/rack-test/rack-test/issues/200 –

Cuestiones relacionadas