Je souhaite envoyer des données de publication brutes (par exemple, JSON non paramétré) à l'un de mes contrôleurs pour les tester:
class LegacyOrderUpdateControllerTest < ActionController::TestCase
test "sending json" do
post :index, '{"foo":"bar", "bool":true}'
end
end
mais cela me donne une erreur NoMethodError: undefined method `symbolize_keys' for #<String:0x00000102cb6080>
.
Quelle est la bonne façon d’envoyer des données de publication brutes dans ActionController::TestCase
?
Voici un code de contrôleur:
def index
post_data = request.body.read
req = JSON.parse(post_data)
end
J'ai rencontré le même problème aujourd'hui et j'ai trouvé une solution.
Dans votre test_helper.rb
, définissez la méthode suivante à l'intérieur 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
Dans votre test fonctionnel, utilisez-le comme la méthode post
, mais transmettez le corps de publication brut en tant que troisième argument.
class LegacyOrderUpdateControllerTest < ActionController::TestCase
test "sending json" do
raw_post :index, {}, {:foo => "bar", :bool => true}.to_json
end
end
J'ai testé cela sur Rails 2.3.4 lors de la lecture du corps de message brut à l'aide de
request.raw_post
au lieu de
request.body.read
Si vous regardez le code source vous verrez que raw_post
encapsule simplement request.body.read
avec une vérification de ce RAW_POST_DATA
dans la valeur de hachage request
env.
J'ai en fait résolu les mêmes problèmes en ajoutant une ligne Avant de simuler la demande de publication rspec. Ce que vous faites remplit "RAW_POST_DATA". J'ai essayé de supprimer Les attributs var sur le post: créer, mais si je le fais, Il ne trouve pas l'action.
Voici ma solution.
def do_create (attributs) request.env ['RAW_POST_DATA'] = attributs.to_json post: créer, attributs fin
Dans le contrôleur, le code dont vous avez besoin pour lire le JSON est Quelque chose de similaire à ceci
@property = Property.new (JSON.parse (request.body.read))
En examinant la trace de pile exécutant un test, vous pouvez acquérir davantage de contrôle sur la préparation de la demande: ActionDispatch :: Integration :: RequestHelpers.post => ActionDispatch :: Integration :: Session.process => (Rack :: Test :: Session.env_for } _
Vous pouvez transmettre une chaîne json en tant que: params ET spécifier un type de contenu "application/json". Dans d'autres cas, le type de contenu sera défini sur "application/x-www-form-urlencoded" et votre json sera analysé correctement.
Il suffit donc de spécifier "CONTENT_TYPE":
post :index, '{"foo":"bar", "bool":true}', "CONTENT_TYPE" => 'application/json'
Si vous utilisez RSpec (> = 2.12.0) et écrivez les spécifications de la demande, le module inclus est ActionDispatch::Integration::Runner
. Si vous regardez le code source, vous remarquerez que le post appelle la méthode process qui accepte un paramètre rack_env
.
Tout cela signifie que vous pouvez simplement faire ce qui suit dans vos spécifications:
#spec/requests/articles_spec.rb
post '/articles', {}, {'RAW_POST_DATA' => 'something'}
Et dans le contrôleur:
#app/controllers/articles_controller.rb
def create
puts request.body.read
end
Pour ceux qui utilisent les tests d'intégration Rails5 +, la méthode (non documentée) consiste à passer une chaîne dans l'argument params, par exemple:
post '/path', params: raw_body, headers: { 'Content-Type' => 'application/json' }
Version pour Rails 5:
post :create, body: '{"foo": "bar", "bool": true}'
Voir ici - body
Le paramètre de chaîne est traité comme un corps de requête brute.
En utilisant Rails 4, je cherchais à faire cela pour tester le traitement du xml brut qui était envoyé au contrôleur. J'ai pu le faire en fournissant simplement la chaîne au message:
raw_xml = File.read("my_raw.xml")
post :message, raw_xml, format: :xml
Je crois que si le paramètre fourni est une chaîne, elle est simplement transmise au contrôleur en tant que corps.
La méthode post
attend un hachage de paires nom-valeur. Vous devrez donc faire quelque chose comme ceci:
post :index, :data => '{"foo":"bar", "bool":true}'
Ensuite, dans votre contrôleur, obtenez les données à analyser comme ceci:
post_data = params[:data]
À partir de Rails 4.1.5, c'était la seule chose qui fonctionnait pour moi:
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
pour une URL à/accounts/5/orders/10/items. Cela récupère les paramètres de l’URL ainsi que le corps JSON. Bien sûr, si les commandes ne sont pas intégrées, vous pouvez ne pas utiliser le hachage 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
Dans Rails, 5.1, les tâches suivantes s’effectuent pour moi lorsqu’une requête de suppression nécessitant des données dans le corps:
delete your_app_url, as: :json, env: {
"RAW_POST_DATA" => {"a_key" => "a_value"}.to_json
}
REMARQUE: Ceci ne fonctionne que lors d'un test d'intégration.
Je cherchais depuis très longtemps comment publier du contenu JSON brut dans un test d'intégration (Rails 5.1). Je suppose que ma solution pourrait également aider dans ce cas… .. J'ai consulté la documentation et le code source de la méthode post
: https://api.rubyonrails.org/v5.1/classes/ActionDispatch/Integration/RequestHelpers .html # method-i-post
Cela m'a dirigé vers la méthode process
pour plus de détails: https://api.rubyonrails.org/v5.1/classes/ActionDispatch/Integration/Session.html#method-i-process
Grâce à cela, j'ai enfin découvert quels paramètres sont acceptés par la méthode process
et donc post
. Voici à quoi ressemblait ma solution finale:
post my_url, params: nil, headers: nil, env: {'RAW_POST_DATA' => my_body_content}, as: :json