Can't pass images as params in Rspec Rails controller spec - ruby-on-rails

Trying to test the create action in post_controller. All other controllers' create actions work just fine.
After some debugging, the error seems to be the params. The images passed from the post factory are blank in the controller
post_spec.rb
describe 'POST #create' do
it 'check post creation' do
new_post = build :post, user: user
attrs = new_post.attributes
attrs.store(:images, [ Rack::Test::UploadedFile.new(image_url, 'image/jpg') ])
post posts_path, params: { post: new_post.attributes }
expect(response).to be_success
end
end
post.rb(model)
class Post < ApplicationRecord
belongs_to :user
has_many :comments, dependent: :destroy
has_many :likes, dependent: :destroy
has_many_attached :images
validates :user_id, presence: true
validates :title, presence: true, length: { minimum: 1, maximum: 1000 }
validates :images, presence: true, limit: { min: 1, max: 10 },
content_type: ['image/jpg', 'image/jpeg', 'image/png', 'image/webp']
end
post.rb (factory)
image_url = '/home/dev/Downloads/a1.jpg'
FactoryBot.define do
factory :post do
title { Faker::Lorem.sentence }
images { Rack::Test::UploadedFile.new(image_url, 'image/jpg') }
user
end
end
post_controller.rb
def create
#post = Post.new(post_params)
if #post.save
redirect_to post_url(#post), notice: 'Post was successfully created.'
else
render :new, status: :unprocessable_entity
end
end
def post_params
params.require(:post).permit(:title, :user_id, images: [])
end
When check for errors on #post here' the output
['Images cannot be blank']
new_post in post_spec
#<Post id: nil, title: "Sequi excepturi quam quos.", user_id: 307, created_at: nil, updated_at: nil>
new_post.attributes in post_spec
{"id"=>nil, "title"=>"Sequi excepturi quam quos.", "user_id"=>307, "created_at"=>nil, "updated_at"=>nil}

The problem is in your
post_controller.rb
def post_params
params.require(:post).permit(:title, :user_id, images: [])
end
you are requiring an array of images, but in test you pass single instance of image.
Either edit your params.require in controller or update your factory (post) file, what suits better.
So now if you change your factory code to
post.rb (factory)
FactoryBot.define do
factory :post do
title { Faker::Lorem.sentence }
images { [Rack::Test::UploadedFile.new('/home/dev/Downloads/a1.jpg', 'image/jpg')] }
user
end
end
You will have your images filled.

Related

I get user, volunteer, and event must exist, when I try to post a student as json data in postman to my rails backend student table

I don't understand why I am getting a 422 on http request, I feel I put all parameters I needed into my request, cant someone tell me what is wrong with this cond.
this is my controllers trusted parameters
# Use callbacks to share common setup or constraints between actions.
def set_user
#user = User.find(params[:id])
end
# Only allow a list of trusted parameters through.
def user_params
params.require(:user).permit(:name, :email, :password)
end
end
I have a user, event, volunteer with id 2
[
{
"id": 2,
"name": "otis guess",
"address": "195 Garfield ave",
"contact": 860303,
"user_id": 2,
"event_id": 2,
"volunteer_id": 2,
"created_at": "2022-09-09T11:25:28.252Z",
"updated_at": "2022-09-09T11:25:28.252Z"
}
]
here is my controller
class StudentsController < ApplicationController
before_action :set_student, only: %i[ show update destroy ]
# GET /students
def index
#students = Student.all
render json: #students
end
# GET /students/1
def show
render json: #student
end
# POST /students
def create
#student = Student.new(student_params)
if #student.save
render json: #student, status: :created, location: #student
else
render json: #student.errors, status: :unprocessable_entity
end
end
# PATCH/PUT /students/1
def update
if #student.update(student_params)
render json: #student
else
render json: #student.errors, status: :unprocessable_entity
end
end
# DELETE /students/1
def destroy
#student.destroy
end
private
# Use callbacks to share common setup or constraints between actions.
def set_student
#student = Student.find(params[:id])
end
# Only allow a list of trusted parameters through.
def student_params
params.require(:student).permit(:name, :address, :contact, :User_id, :Event_id, :Volunteer_id)
end
end
response is 422
{
"user": [
"must exist"
],
"event": [
"must exist"
],
"volunteer": [
"must exist"
]
}
What is wrong with this request, as I feel all of it is fine.
{
"student": {
"name": "Dom",
"address": "112 connecticut ave",
"contact": "86030",
"user_id": 2,
"event_id": 2,
"volunteer_id": 2
}
}
model
class User < ApplicationRecord
has_many :students, dependent: :destroy
has_many :events, dependent: :destroy
has_many :volunteers, dependent: :destroy
# validates :username, presence: true, uniqueness: true
# validates :email, presence: true, uniqueness: true
# validates :email, format: { with: URI::MailTo::EMAIL_REGEXP }
# validates :password, length: { minimum: 6 }
end

Rails controller test error

I'am making a controller test, but I have this error. My model tests passing, but about this I don't know what is going on:
/cook-book/test/controllers/recipes_controller_test.rb:23]: "Recipe.count" didn't change by 1 Expected: 3 Actual: 2
my 'recipes_controller_test.rb':
require 'test_helper'
class RecipesControllerTest < ActionController::TestCase
include Devise::TestHelpers
setup do
#recipe = recipes(:one)
user = User.create!(email: "example2#test.com", password: "password",password_confirmation: "password" )
sign_in user
end
....
test "should create recipe" do
assert_difference('Recipe.count') do
post :create, recipe: { title: #recipe.title, image_file_name: #recipe.image_file_name, ingredients: #recipe.ingredients, description: #recipe.description }
end
assert_redirected_to recipe_path(assigns(:recipe))
end
'recipes.yml':
one:
title: MyString
image_file_name: Myimage
ingredients: MyText
description: MyText
two:
title: MyString
image_file_name: Myimage
ingredients: MyText
description: MyText
and recipe model:
class Recipe < ActiveRecord::Base
belongs_to :category
belongs_to :user
has_many :comments
has_attached_file :image, styles: { medium: "400x400#", small: "250x250#"}
validates_attachment_content_type :image, content_type: /\Aimage\/.*\Z/
validates :title, presence: true, length: { maximum: 30 }
validates :ingredients, presence: true
validates :description, presence: true
validates :image, presence: true
searchkick
end
#Edit
my 'recipe_controller'
Class RecipesController < ApplicationController
before_action :find_recipe, only: [ :show, :edit, :update, :destroy ]
before_action :authenticate_user!, except: [:index, :show]
.....
def create
#recipe = current_user.recipes.build(recipe_params)
if #recipe.save
redirect_to #recipe
else
render 'new'
end
end
private
def find_recipe
#recipe = Recipe.find(params[:id])
end
def recipe_params
params.require(:recipe).permit(:title, :ingredients, :description, :image, :category_id)
end
Thanks!

Rspec and FactoryGirl: stack level too deep error

I'm new to rails and working on embedding associations into my models for my API. However adding the embeds has caused my specs to fire the following error:
Rails: 4.2.3 Ruby:2.2.1 Rspec: 3.3.2 FactoryGirl: 4.5.0
1) Api::V1::ProductsController GET #show returns the information about a reporter on a hash
Failure/Error: get :show, id: #product.id
SystemStackError:
stack level too deep
I think from looking at other answers on stack overflow that there is a problem with how I'm using my factories in my tests.
Here are the models:
product.rb
class User < ActiveRecord::Base
validates :auth_token, uniqueness: true
# Include default devise modules. Others available are:
# :confirmable, :lockable, :timeoutable and :omniauthable
devise :database_authenticatable, :registerable,
:recoverable, :rememberable, :trackable, :validatable
before_create :generate_authentication_token!
has_many :products, dependent: :destroy
def generate_authentication_token!
begin
self.auth_token = Devise.friendly_token
end while self.class.exists?(auth_token: auth_token)
end
end
user.rb
class Product < ActiveRecord::Base
validates :title, :user_id, presence: true
validates :price, numericality: { greater_than_or_equal_to: 0 },
presence: true
belongs_to :user
end
and the factories:
users.rb
FactoryGirl.define do
factory :user do
email { FFaker::Internet.email }
password "12345678"
password_confirmation "12345678"
end
end
products.rb
FactoryGirl.define do
factory :product do
title { FFaker::Product.product_name }
price { rand() * 100 }
published false
user
end
end
and here is the spec I'm getting all of the errors from.
products_controller_spec.rb
require 'spec_helper'
describe Api::V1::ProductsController do
describe "GET #show" do
before(:each) do
#product = FactoryGirl.create :product
get :show, id: #product.id
end
it "returns the information about a reporter on a hash" do
product_response = json_response[:product]
expect(product_response[:user][:email]).to eql #product.user.email
end
it "has the user as an embeded object" do
product_response = json_response[:product]
expect(product_response[:user][:email]).to eql #product.user.email
end
it { should respond_with 200 }
end
describe "GET #index" do
before(:each) do
4.times { FactoryGirl.create :product }
get :index
end
it "returns 4 records from the database" do
products_response = json_response
expect(products_response[:products]).to have(4).items
end
it "returns the user object into each product" do
products_response = json_response[:products]
products_response.each do |product_response|
expect(product_response[:user]).to be_present
end
end
it { should respond_with 200 }
end
describe "POST #create" do
context "when is succesfully created" do
before(:each) do
user = FactoryGirl.create :user
#product_attributes = FactoryGirl.attributes_for :product
api_authorization_header user.auth_token
post :create, { user_id: user.id, product: #product_attributes }
end
it "renders the json representation for the product record just created" do
product_response = json_response[:product]
expect(product_response[:title]).to eql #product_attributes[:title]
end
it { should respond_with 201 }
end
context "when is not created" do
before(:each) do
user = FactoryGirl.create :user
#invalid_product_attributes = { title: "Smart TV", price: "Twelve dolalrs" }
api_authorization_header user.auth_token
post :create, { user_id: user.id, product: #invalid_product_attributes }
end
it "renders an errors json" do
product_response = json_response
expect(product_response).to have_key(:errors)
end
it "renders the json errors on why the product could not be created" do
product_response = json_response
expect(product_response[:errors][:price]).to include "is not a number"
end
it { should respond_with 422 }
end
end
describe "PUT/PATCH #update" do
before(:each) do
#user = FactoryGirl.create :user
#product = FactoryGirl.create :product, user: #user
api_authorization_header #user.auth_token
end
context "when is successfully updated" do
before(:each) do
patch :update, { user_id: #user.id, id: #product.id,
product: { title: "An expensive TV"} }
end
it "renders the json representation for the updated user" do
product_response = json_response[:product]
expect(product_response[:title]).to eql "An expensive TV"
end
it { should respond_with 200 }
end
context "when is not updated" do
before(:each) do
patch :update, { user_id: #user.id, id: #product.id,
product: { price: "two hundred" } }
end
it "renders an errors json" do
product_response = json_response
expect(product_response).to have_key(:errors)
end
it "renders the json errors on why the user could not be created" do
product_response = json_response
expect(product_response[:errors][:price]).to include "is not a number"
end
it { should respond_with 422 }
end
end
describe "DELETE #destroy" do
before(:each) do
#user = FactoryGirl.create :user
#product = FactoryGirl.create :product, user: #user
api_authorization_header #user.auth_token
delete :destroy, { user_id: #user.id, id: #product.id }
end
it { should respond_with 204 }
end
end
And the Products Controller:
class Api::V1::ProductsController < ApplicationController
before_action :authenticate_with_token!, only: [:create, :update]
respond_to :json
def show
respond_with Product.find(params[:id])
end
def index
respond_with Product.all
end
def create
product = current_user.products.build(product_params)
if product.save
render json: product, status: 201, location: [:api, product]
else
render json: { errors: product.errors }, status: 422
end
end
def update
product = current_user.products.find(params[:id])
if product.update(product_params)
render json: product, status: 200, location: [:api, product]
else
render json: { errors: product.errors }, status: 422
end
end
def destroy
product = current_user.products.find(params[:id])
product.destroy
head 204
end
private
def product_params
params.require(:product).permit(:title, :price, :published)
end
end
user_serializer.rb
class UserSerializer < ActiveModel::Serializer
attributes :id, :email, :created_at, :updated_at, :auth_token
has_many :products
end
EDIT: Adds Products Controller

creating an object with has_many association results in item can not be blank

I have following associations and the related controller, in my form I am adding every field as it should be. But I still get an error Ratings item can't be blank when I try to create an Item. I am using Rails 4.0 . I did searched extensively for this but could not still find what I am doing wrong. Thankyou!
class Item < ActiveRecord::Base
has_many :ratings, dependent: :destroy
accepts_nested_attributes_for :ratings, :allow_destroy => true
validates :name , :length => { minimum: 3 }
validates :category , :length => { minimum: 3 }
end
class Ratings < ActiveRecord::Base
belongs_to :user
belongs_to :item
default_scope -> { order('created_at DESC') }
validates :user_id, :presence => true
validates :item_id, :presence => true
validates_numericality_of :rating, :greater_than_or_equal_to => 0
validates_numericality_of :rating, :less_than_or_equal_to => 5
end
class ItemsController < ApplicationController
before_action :set_item, only: [:show]
before_action :user_signed_in?, only: :create
def create
#item = Item.new
#rating = #item.ratings.build
#rating.comment = params[:item][:ratings_attributes][:comment]
#rating.rating = params[:item][:ratings_attributes][:rating]
#rating.user_id = current_user.id
#item.name = params[:item][:name]
#item.url = params[:item][:url]
#item.full_address = params[:item][:full_address]
#item.city = params[:item][:city]
#item.country = params[:item][:country]
#item.category = params[:item][:category]
respond_to do |format|
if #item.save
#TODO create rating here (First rating of an Item)
flash[:success] = "Welcome to inmyopnion"
format.html { redirect_to #item, notice: 'Item was successfully created.' }
format.json { render action: 'show', status: :created, location: #item }
else
format.html { render action: 'new' }
format.json { render json: #item.errors, status: :unprocessable_entity }
end
end
end
def new
#item = Item.new
end
def show
end
def destroy
end
private
def set_item
#item = Item.find(params[:id])
end
def item_params
params.require(:item).permit(:name, :url, :full_address, :city, :country, :category, :ratings_attributes => [:rating, :comment])
end
def user_signed_in?
#TODO: should display should sign in to rate an item
redirect_to(root_url) unless signed_in?
end
end
Simplify your controller! Since you are allowing nested_attributes this should be sufficient:
#item = Item.create(params[:item])
The problem might be caused by #rating object not being saved.
I got it working by commenting the below given line in
class Ratings < ActiveRecord::Base
validates :item_id, :presence => true
but my association rspec test fails and saves a Ratings without an item_id.
Rest of the code is similar to what I posted as
#item = Item.create(params[:item])
gives ActiveModel::ForbiddenAttributesError
Alright much playing with the code and docs of nested_attributes finally a working program that validates association too. These are the changes (marked in between ** .... **) listed below
class Item < ActiveRecord::Base
has_many :ratings, dependent: :destroy, **inverse_of: :item**
accepts_nested_attributes_for :ratings, :allow_destroy => true
validates :name , :length => { minimum: 3 }
validates :category , :length => { minimum: 3 }
end
class Ratings < ActiveRecord::Base
belongs_to :user
belongs_to :item, **inverse_of: :ratings**
default_scope -> { order('created_at DESC') }
validates :user_id, :presence => true
validates_presence_of :item
validates_numericality_of :rating, :greater_than_or_equal_to => 0
validates_numericality_of :rating, :less_than_or_equal_to => 5
end
Still not able to create one from #item = Item.create(params[:item]) which still gives an gives
ActiveModel::ForbiddenAttributesError as suggested by #BroiSatse and also the docs of nested_attributes that should not be the case
the problem might be in
class ItemsController < ApplicationController
def item_params
params.require(:item).permit(:name, :url, :full_address, :city, :country, :category, :ratings_attributes => [:rating, :comment])
end
will work on to resolve that too and post an answer if I find a solution.

Cannot favorite users using polymorphic association

Using the system below users are able to favorite styles in the format of:
=> #<Favorite id: 59, user_id: 2, favoritable_id: 15, favoritable_type: "Style", created_at: "2013-04-06 08:47:08", updated_at: "2013-04-06 08:47:08">
I have built the system to enable users to favorite users as well, but for some reason the line Favorite.create!(:user => user) is not creating the expected favorite model object when called on User, as above when called on Style. Instead I am getting nils as below:
=> #<Favorite id: 65, user_id: 2, favoritable_id: nil, favoritable_type: nil, created_at: "2013-04-06 09:35:07", updated_at: "2013-04-06 09:35:07">
Problem is above. Details are below
Favorite Class:
class Favorite < ActiveRecord::Base
belongs_to :favoritable, :polymorphic => true
belongs_to :user
attr_accessible :user
validates_presence_of :user
validates_uniqueness_of :user_id, :scope => [:favoritable_type, :favoritable_id]
end
Model definitions including favoritable module (see below):
class User < ActiveRecord::Base
include Favoritable
class Style < ActiveRecord::Base
include Favoritable
Favoritable Module:
module Favoritable
extend ActiveSupport::Concern
included do
attr_accessible :favorites
has_many :favorites, :as => :favoritable, :autosave => true
accepts_nested_attributes_for :favorites
def add_favorite(user)
self.favorites << Favorite.create!(:user => user)
save
end
def delete_favorite(user)
self.favorites.where(:user_id => user.id).each do |favorite|
favorite.destroy
end
end
end
end
Favorites Controller:
class FavoritesController < ApplicationController
def fav_user
#user = User.find(params[:id])
#user.add_favorite(current_user)
respond_to do |format|
format.js { render :action => "update_favorite_disp", :layout => false }
end
end
def delete_fav_user
#user = User.find(params[:id])
respond_to do |format|
if #user.delete_favorite(current_user)
format.js { render :action => "update_favorite_disp", :layout => false }
end
end
end
def fav_style
#style = Style.find(params[:id])
#style.add_favorite(current_user)
respond_to do |format|
format.js { render :action => "update_favorite", :layout => false }
end
end
def delete_fav_style
#style = Style.find(params[:id])
respond_to do |format|
if #style.delete_favorite(current_user)
format.js { render :action => "update_favorite", :layout => false }
end
end
end
end
You need to pass in the :favoritable model in add_favorite:
def add_favorite(user)
Favorite.create!(:user =>user, :favoritable => self)
save
delete_favorite also needs to delete based on the :favoritable_id, instead of :user_id:
def delete_favorite(user)
user.favorites.where(:favoritable_id => self.id).each do |favorite|
favorite.destroy
end
end

Resources