tvieira
tvieira

Reputation: 1915

RSpec not sending a Post request in JSON

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

Answers (6)

nitrnitr
nitrnitr

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

reiallenramos
reiallenramos

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

nicb
nicb

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

Jeff
Jeff

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

monteirobrena
monteirobrena

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

jvillian
jvillian

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

Related Questions