Reputation: 1915
I'm doing a Rails App that it's mostly an API. I'm trying to test the controllers for my API endpoints. My RSpec Controller tester is as following:
require 'rails_helper'
require 'spec_helper'
require 'rack/test'
require 'devise'
class RoutesControllerTest < ActionController::TestCase
describe "User with token"
test "should post route" do
params = { route: { start_lat: 38.7627951, start_long: -9.1532211,
end_lat: 38.7483783, end_long: -9.155045,
flag_opt: 0.4, city: 1 }
}
post '/routes.json' , params.to_json, format: :json
assert_response :success
end
end
end
And My Controller is:
class RoutesController < ApplicationController
def create
city = City.find(params[:route][:city])
user = current_user
@route = user.routes.new(route_params)
@results = @route.calc_route(@route.start_long, @route.start_lat, @route.end_long, @route.end_lat, params[:route][:flag_opt], city)
if @route.save!
render :template=>"/routes/routes.json.jbuilder", status: 201, :formats => [:json]
else
render json: @route.errors
end
end
private
def route_params
json_params = ActionController::Parameters.new( JSON.parse(request.body.read) )
json_params.require(:route).permit(
:start_lat,
:start_long,
:end_lat,
:end_long,
:flag_opt
)
end
end
But everytime I run the rspec spec/controller
I get into the following error:
Failure/Error: json_params = ActionController::Parameters.new(JSON.parse(request.body.read) )
JSON::ParserError:
757: unexpected token at 'route%5Bcity%5D=24&route%5Bend_lat%5D=41.26171490000001&route%5Bend_long%5D=-8.38193640000001&route%5Bflag_opt%5D=1&route%5Bstart_lat%5D=38.753225&route%5Bstart_long%5D=-9.144376&route%5Buser_id%5D=24'
Which means that the request is not being sent as JSON
Upvotes: 3
Views: 8541
Reputation: 488
This worked for me to test a json posted on the body to the controller (RSpec 3.7.0)
post :action, params: { whatever: value }, body: { some_key: value }.to_json, as: :json
And you read your controller with
request.body.read
Upvotes: 4
Reputation: 1295
for Rails 5, just call request.accept = "application/json"
before the HTTP verb. Example:
context "GET #show" do
it "returns json data" do
request.accept = "application/json"
get :show
hash_body = JSON.parse(response.body)
#### your expectation here
end
end
Upvotes: 0
Reputation: 321
I've been struggling with this one too. Sometimes you really just want to test with a real JSON body. I'm not sure if this solution is specific to Rails 5, but after a lot of hunting I was relieved to finally figure this one out.
You can use as: :json
instead of format: :json
to make RSpec format the request body.
That is, change:
post '/routes.json' , params.to_json, format: :json
to
post '/routes.json' , params.to_json, as: :json
Upvotes: 9
Reputation: 660
I'm not sure why your API has to receive json in it's create action. Here's what I would do:
class RoutesControllerTest < ActionController::TestCase
describe "User with token"
test "should post route" do
params = { route: { start_lat: 38.7627951, start_long: -9.1532211,
end_lat: 38.7483783, end_long: -9.155045,
flag_opt: 0.4, city: 1 }
}
count = Route.all.size
post :create, params
expect(response).to have_http_status(201)
expect(Route.all.size).to eq(count + 1) # actually created something
end
end
end
And then in your strong params:
def route_params
params.require(:route).permit(
:start_lat,
:start_long,
:end_lat,
:end_long,
:flag_opt
)
end
No need to process everything as JSON even on the rails end.
Upvotes: 0
Reputation: 2620
You need set the JSON parse to your test environment.
Create a file in spec/support/request_helper.rb:
module Requests
module JsonHelpers
def json
@json ||= JSON.parse(response.body)
end
end
end
RSpec.configure do |config|
config.include Requests::JsonHelpers, type: :controller
end
And
Upvotes: 0
Reputation: 20263
I'm curious why you are doing json_params = ActionController::Parameters.new( JSON.parse(request.body.read)
? (Am I missing something?)
params should already come in as ActionContoller::Parameters. So, route_params should be
def route_params
params.require(:route).permit(
:start_lat,
:start_long,
:end_lat,
:end_long,
:flag_opt
)
end
By the way, I assume your routes.rb is something like
# config/routes.rb
Rails.application.routes.draw do
namespace :api, defaults: {format: 'json'} do
...
end
end
Upvotes: 1