How to send location after `create` action - ruby-on-rails

I have the following create action:
def create
episode = Episode.new(episode_params)
if episode.save
render json: episode, status: :created, location: episode
end
end
but when I test the following:
require 'test_helper'
class CreatingEpisodesTest < ActionDispatch::IntegrationTest
setup { host! 'api.example.com'}
test 'create episodes' do
post '/episodes',
{ episode:
{ title: 'Bananas', description: 'Learn about bananas.' }
}.to_json,
{ 'Accept' => Mime::JSON, 'Content-Type' => Mime::JSON.to_s }
assert_equal 200, response.status
assert_equal Mime::JSON, response.content_type
episode = json(response.body)
assert_equal "/episodes/#{episode[:id]}", response.location
end
end
I get the following error:
1) Error: CreatingEpisodesTest#test_create_episodes: NoMethodError: undefined method `episode_url' for #<API::EpisodesController:0x007f8e34519450> Did you mean? episode_params
app/controllers/api/episodes_controller.rb:11:in `create'
test/integration/creating_episodes_test.rb:7:in `block in #<class:CreatingEpisodesTest>'
1 runs, 0 assertions, 0 failures, 1 errors, 0 skips
I guest I miss something to send the location after I create the episode.
UPDATE: Route
Rails.application.routes.draw do
namespace :api, path: '/', constraints: { subdomain: 'api' } do
resources :zombies
resources :episodes
end
end

This is because of namespace, use [:api, episode] for location

Related

Rails request spec - undefined method `body' for nil:NilClass (rswag)

I'm trying to generate API documentation with rwsag, and I'm confused about a couple of my specs failing.
Here is my request spec:-
require 'swagger_helper' ### this includes 'rails_helper'
RSpec.describe 'api/v1/users', type: :request do
before(:each) { host! 'localhost:3001' }
path '/api/v1/users' do
post('create user') do
tags 'users'
consumes 'application/json'
parameter name: :user, in: :body, schema: {
type: :object,
properties: {
title: { type: :string },
description: { type: :string },
date: { type: :datetime },
budget: { type: :decimal },
awarded: { type: :boolean }
},
required: [ 'title', 'description' ]
}
response(200, 'successful') do
after do |example|
example.metadata[:response][:content] = {
'application/json' => {
example: JSON.parse(response.body, symbolize_names: true)
}
}
end
run_test!
end
end
end
Here are my two errors which don't make much sense to me:-
1) api/v1/users /api/v1/users post successful returns a 200 response
Got 0 failures and 2 other errors:
1.1) Failure/Error: super
NoMethodError:
undefined method `user' for #<RSpec::ExampleGroups::ApiV1Users::ApiV1Users::Post::Successful:0x0000aaaad7d06600>
# /usr/local/bundle/gems/rswag-specs-2.4.0/lib/rswag/specs/request_factory.rb:197:in `build_json_payload'
# /usr/local/bundle/gems/rswag-specs-2.4.0/lib/rswag/specs/request_factory.rb:180:in `add_payload'
# /usr/local/bundle/gems/rswag-specs-2.4.0/lib/rswag/specs/request_factory.rb:22:in `block in build_request'
# /usr/local/bundle/gems/rswag-specs-2.4.0/lib/rswag/specs/request_factory.rb:18:in `tap'
# /usr/local/bundle/gems/rswag-specs-2.4.0/lib/rswag/specs/request_factory.rb:18:in `build_request'
# /usr/local/bundle/gems/rswag-specs-2.4.0/lib/rswag/specs/example_helpers.rb:10:in `submit_request'
# /usr/local/bundle/gems/rswag-specs-2.4.0/lib/rswag/specs/example_group_helpers.rb:94:in `block in run_test!'
1.2) Failure/Error: example: JSON.parse(response.body, symbolize_names: true)
NoMethodError:
undefined method `body' for nil:NilClass
# ./spec/requests/api/v1/users_spec.rb:84:in `block (5 levels) in <main>'
And my standard controller action:-
module Api
module V1
class UsersController < ApplicationController
# POST /api/v1/users
def create
#user = Job.new(user_params)
if #user.save
render json: #user, status: :created
else
render json: #user.errors, status: :unprocessable_entity
end
end
Any idea what is causing this error? I'm also a little confused with the generated rswag spec structure - i.e. no expects anywhere.
The answer is that I was missing the below let(:user) line:-
response(201, 'successful') do
after do |example|
example.metadata[:response][:content] = {
'application/json' => {
example: JSON.parse(response.body, symbolize_names: true)
}
}
end
let(:user) { { title: 'foo', description: 'bar' } }
run_test!
end

RSpec returns empty string in a fairly simple get request

I want to test show action in my Shipment controller. To do so I've prepared fairly simple specs:
RSpec.describe ShipmentsController, type: :controller do
describe 'GET #show' do
let(:params) { { id: shipment.id, product_id: product.id } }
let!(:product) { create(:product) }
let!(:shipment) { create(:shipment, product: product) }
context 'when params are valid' do
before { get :show, params: params }
it 'return valid json' do
expect(JSON.parse(response.body)).to eq(expected_json)
end
end
end
end
ShimpentsController.rb
class ShipmentsController < ApplicationController
before_action :set_product
attr_reader :shipment
def show
#shipment = Shipment.find(params[:id])
#items = shipment&.grouped_shipment_items
end
private
def set_product
#product = Product.find(params[:product_id])
end
end
When I use postman everything went well - it returns expected json but in the RSpec test I'm getting:
response.body
=> ""
I think you need to add render_views in your controller spec file.
RSpec.describe ShipmentsController, type: :controller do
render_views
describe 'GET #show' do
let(:params) { { id: shipment.id, product_id: product.id } }
let!(:product) { create(:product) }
let!(:shipment) { create(:shipment, product: product) }
context 'when params are valid' do
before { get :show, params: params }
it 'return valid json' do
expect(JSON.parse(response.body)).to eq(expected_json)
end
end
end
end
Reference: https://rubyinrails.com/2019/04/11/rails-test-jbuilder-json-response-with-rspec/
I think you are not making a request for JSON response with rspec. You can check by putting a breakpoint in your controller action, then checking
request.format.json?
In order to ask for JSON response from an rspec test, you should add as: :json to the end of the request. The request should look like this:
get :show, params: params, as: :json

ActionController::UrlGenerationError: No route matches (Rspec)

I am getting the following error while testing my CommentsController with RSpec:
ActionController::UrlGenerationError:
No route matches {:action=>"create", :comment=>{:comment=>"aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"}, :controller=>"comments"}
spec/models/comment_spec.rb
RSpec.describe CommentsController, type: :controller do
let!(:user) { create(:user) }
let!(:post1) { create(:post, user: user) }
let!(:comment) { create(:comment, user_id: user.id, post_id: post1.id) }
let!(:comment_attributes) { attributes_for(:comment) }
describe "#create" do
before do
sign_in user
end
it 'save post' do
expect do
post :create, params: { comment: comment_attributes }, session: {}
end.to change(Comment, :count).by(1)
end
it 'if post saves, redirect_to posts page' do
post :create, params: { post: comment_attributes }, session: {}
expect(response).to redirect_to(posts_path)
end
end
end
You have to update your routes.rb file every time you create a new resource (that you want to access via link), otherwise Rails don't know which controller to use with a given URL. Adding a resources :comments line in routes.rb should do it for you.

Substitution of request.formats: ["text/html"] with ["application/javascript"]

That`s my carts_controller.rb. By default controller action add renders add.js.erb in /app/views/carts/
class CartsController < ApplicationController
def add
find_cart_and_product
#cart.products << #product
CartMailer.product_added(#product).deliver_now
end
end
and my test
describe 'POST #add' do
let(:cart_full_of){ create(:cart_with_products) }
let(:product){ create(:product) }
before do
post :add, session: { cart_id: cart_full_of.id }, params: { product_id: product.id}
end
it { expect(response.status).to eq(200) }
it { expect(response.headers["Content-Type"]).to eql("application/javascript"; charset=utf-8")}
it { is_expected.to render_template :add }
it 'should add current product into cart' do
expect(cart_full_of.products).to eq([product])
end
end
has failed with common error for all test items:
Failure/Error: post :add, session: { cart_id: cart_full_of.id }, params: { product_id: product.id}
ActionController::UnknownFormat:
CartsController#add is missing a template for this request format and variant.
request.formats: ["text/html"]
request.variant: []
I consider problem with expected requesting format, so how to force tests render with request.formats: ["application/javascript"] instead ["text/html"]?
This works for me (Rspec 3):
before :each do
request.headers["accept"] = 'application/javascript'
end

Issue with apipie gem and rspec in rails 4

i'm writing the code to get my Rspec tests to pass on my api. I'm using the apipie gem to generate documentation and it seems that my tests are failing because thy are expecting a number and it's funny because this is exactly what I want to test.
The page fails when the :bpm parameter is not a number. is there any way of going around this ?
context "when is not created" do
before(:each) do
user = FactoryGirl.create :user
#invalid_lesson_attributes = { title: "California Dreamin",
bpm: "Hello"
}
request.headers['Authorization'] = user.auth_token
post :create, { user_id: user.id, lesson: #invalid_lesson_attributes }
end
it "renders an errors json" do
lesson_response = json_response
expect(lesson_response).to have_key(:errors)
end
it "renders the json errors on why the user could not be created" do
lesson_response = json_response
expect(lesson_response[:errors][:bpm]).to include "is not a number"
end
it { should respond_with 422 }
end
end
Update spec:
context "when is not updated" do
before(:each) do
patch :update, { user_id: #user.id, id: #lesson.id,
lesson: { bpm: "ten" }, format: :json }
end
it "renders an errors json" do
lesson_response = json_response
expect(lesson_response).to have_key(:errors)
end
it "renders the json errors on why the user could not be updated" do
lesson_response = json_response
expect(lesson_response[:errors][:bpm]).to include "is not a number"
end
it { should respond_with 422 }
end
in my users_controller:
api :POST, '/teachers/:user_id/lessons/', "Create lesson"
param :lesson, Hash, desc: 'Lesson information', :required => true do
param :title, String, desc: 'Title of the lesson', :required => true
param :bpm, :number, desc: 'tempo of the lesson (beats per second)', :required => true
end
error :code => 422, :desc => "Unprocessable Entity"
my error when I run my rspec tests :
Apipie::ParamInvalid: Invalid parameter 'bpm' value "Hello": Must be a number.
Adds format json to post request
post :create, { user_id: user.id, lesson: #invalid_lesson_attributes, format: :json }
That worked for me.

Resources