Models:
class User < ApplicationRecord
has_many :posts
end
class Post < ApplicationRecord
belongs_to :user
end
Controllers:
class PostsController < ApplicationController
def create
#post = current_user.posts.creata(post_params)
end
def post_params
params.require(:post).permit(:name,:user_id)
end
end
RSpec.describe PostsController do
describe "create post" do
it "should create"
user = create(:user)
expect{
post :create, params: { name: "new post", user_id: user.id }
}.to change(Post,:count).by(1)
end
end
And this error:
ActionController::ParameterMissing:
param is missing or the value is empty: post
On rails 5 you need pass the whole params structure. See below:
Rails 4:
post :create, post: { name: "new post" }
Rails 5:
post :create, params: { post: { name: "new post" } }
Your specs is also missing the current_user stub:
RSpec.describe PostsController do
describe "create post" do
before do
user = create(:user)
allow_any_instance_of(PostsController).to receive(:current_user) { user }
end
it "should create"
expect {
post :create, params: { post: { name: "new post" } }
}.to change(Post, :count).by(1)
end
end
Related
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
I'm having problems to pass a test in rails with rspec. This is what console tells me when I ran the tests.
The fail is ControlsController GET index logged in renders the index template
Failure/Error: expect(response). to render_template(:index)
expecting <"index"> but rendering with <[]>
And this is my code
require "rails_helper"
RSpec.describe ControlsController, :type => :controller do
render_views
describe "GET index" do
let(:user) {
FactoryGirl.create(:user)
}
let(:control) {
FactoryGirl.create(:control, user: user)
}
context "logged in" do
before :each do
sign_in :user, user
end
it "loads all controls into #controls" do
get :index, { user_id: user.id}
expect(assigns(:controls)).to eq([control])
end
it "assigns a new control to #control" do
get :index, { user_id: user.id}
expect(assigns(:control)).to be_a_new(Control)
end
it "renders the index template" do
get :index, { user_id: user.id}
expect(response). to render_template(:index)
end
it "a user can't see the controls from other user" do
new_user = User.create(name: "Juan",
email: "juan#gmail.com",
password: "123456789",
password_confirmation: "123456789")
get :index, { user_id: new_user.id}
expect(response).to redirect_to root_path
end
class ControlsController < ApplicationController
before_action :authenticate_user!
def index
#user= current_user
#control= Control.new
# #control_last = Control.lastcontrol (current_user.id)
# #controls_average = Control.controls_average (current_user.id)
# #controls_average_day = Control.controls_day_average (current_user.id)
#controls = Control.all
if params[:user_id] != current_user.id
redirect_to root_path
end
end
The answer is to make a private method and redirect_to user_controls_path current_user.name
This is the new code of the controller
controlsController.rb
class ControlsController < ApplicationController
before_action :authenticate_user!
before_action :redirect_if_not_current_user, only: :index
private
def control_params
params.require(:control).permit(:level, :period, :day)
end
def redirect_if_not_current_user
if params[:user_id] != current_user.name
redirect_to user_controls_path current_user.name
end
end
I'm trying to write a controller test for my update route using Rspec with factory girl but I can't get my test to pass validations even though I'm pretty sure the data in my factories is valid.
Here are my factories:
FactoryGirl.define do
factory :user do
username { Faker::Internet.user_name(8) }
password 'password'
end
factory :post do
title { Faker::Lorem.sentence }
body { Faker::Lorem.paragraph }
author { Faker::Internet.user_name(8) }
end
end
here is my model:
class Post < ActiveRecord::Base
validates :title, :body, :author, presence: true
end
Here is my test:
require 'rails_helper'
describe PostsController do
let!(:user) { FactoryGirl.create :user }
let!(:post) { FactoryGirl.create :post }
let(:attributes) { FactoryGirl.attributes_for :post }
describe 'PUT #update' do
let(:title) { "A treatise on Malomars." }
it 'updates a field on a blog post' do
put :update, id: post.id, post: {title: title}
expect(post.reload.title).to eq(post.title)
end
end
end
The error I'm getting is:
Failure/Error: put :update, id: post.id, post: {title: title}
ActiveRecord::RecordInvalid:
Validation failed: Body can't be blank
EDIT---
here is the controller:
class PostsController < ApplicationController
def index
#posts = Post.all
end
def new
end
def create
post = Post.new
post.title = params[:title]
post.body = params[:body]
post.author = "#{session[:username].titleize} Force"
redirect_to root_path
post.save!
end
def show
p session[:id]
#post = Post.find(params[:id])
end
def update
post = Post.find(params[:id])
post.title = params[:post][:title]
post.body = params[:post][:body]
post.save!
redirect_to root_path
end
def destroy
post = Post.find(params[:id])
post.destroy
redirect_to root_path
end
end
Strong parameters aside, you're setting post.body to nil since you're not passing a value for the body parameter in your test. When you call save! in your controller, you're getting an error because you have a validation for body being present (i.e. not nil).
I test my destroy and update methods in hotel_controller and Im keep getting ActiveRecord:RecordNotFound error. Heres a screenshot
I think this is coz FactoryGirs doesnt save recods to the db. Help me pls to get things right.
hotels_controller.rb
class HotelsController < ApplicationController
before_action :signed_in_user, except: [:index, :show, :top5hotels]
...
def destroy
#hotel = current_user.hotels.find(params[:id])
#hotel.destroy
redirect_to hotels_url
end
def update
#hotel = current_user.hotels.find(params[:id])
if #hotel.update_attributes!(params[:hotel])
redirect_to #hotel, notice: "Hotel was successfully updated."
else
render "edit"
end
end
...
end
factories.rb
FactoryGirl.define do
factory :hotel do
name 'NewHotel'
star_rating 5
breakfast false
room_description 'Room Description'
price_for_room 500
user { create(:user) }
address { create(:address) }
end
factory :user do
sequence(:email) { |n| "user_mail.#{n}#gmail.com" }
name 'Yuri Gagarin'
password 'foobar'
password_confirmation 'foobar'
end
factory :rating do
value 5
user { create(:user) }
hotel { create(:hotel) }
end
factory :comment do
body "Heresanytextyouwant"
user { create(:user) }
hotel { create(:hotel) }
end
factory :address do
country 'Country'
state 'State'
city 'City'
street 'Street'
end
end
hotels_controller_spec.rb
require 'spec_helper'
describe HotelsController do
before { sign_in user, no_capybara: true }
...
describe "destroy action" do
it "redirects to index action when hotel is destroyed" do
hotel = create(:hotel)
delete :destroy, id: hotel.id
expect(response).to redirect_to(hotels_url)
end
end
describe "update action" do
it "redirects to the hotel" do
hotel = create(:hotel)
put :update, id: hotel.id, hotel: FactoryGirl.attributes_for(:hotel)
expect(assigns(:hotel)).to be_eq(hotel)
#expect(response).to render_template('show')
end
end
end
FactoryGirl IS saving records to db.
The trouble is the current_user is not the same user that the hotel belongs to, so when you try to retrieve the hotel record it's not found.
Try changing...
#hotel = current_user.hotels.find(params[:id])
to...
#hotel = Hotel.find(params[:id])
And you'll see it works.
If you want to keep the original code, then in the test you should be doing...
hotel = create(:hotel, user: current_user)
I want to test comments controller, action create, but I don't know how do this.
comments_controller
class CommentsController < ApplicationController
def create
#hotel = Hotel.find(params[:hotel_id])
#comment = #hotel.comments.new(comment_params)
#comment.user_id = current_user.id
#comment.save
redirect_to #hotel
end
private
def comment_params
params.require(:comment).permit(:user_id, :body, :hotel_id)
end
end
routes.rb
resources :hotels do
resources :comments
get 'list', on: :collection
post 'comment'
end
comments_controller_spec.rb
require 'rails_helper'
describe CommentsController do
login_user
describe 'POST create' do
it 'create a new comment with valid attributes' do
expect{
comment_attr = attributes_for(:comment)
post :create, comment: comment_attr
}.to change(Comment,:count).by(1)
end
it 'redirects to the new comment' do
comment_attr = attributes_for(:comment)
post :create, comment: comment_attr
expect(response).to redirect_to hotels_path(hotel)
end
end
end
factories.rb
FactoryGirl.define do
factory :user do |user|
user.email { Faker::Internet.email }
user.password 'password'
user.password_confirmation 'password'
end
factory :hotel do |hotel|
hotel.title 'Hotel'
hotel.description 'This is a some description for hotel'
hotel.breakfast true
hotel.price 20500
hotel.address { create(:address) }
hotel.user { create(:user) }
hotel.avatar { fixture_file_upload(Rails.root + 'spec/fixtures/images/example.jpg', "image/jpg") }
end
factory :comment do |comment|
comment.body 'This is a some comment ^_^'
comment.user { create(:user) }
comment.hotel { create(:hotel) }
end
factory :address do |address|
address.country { Faker::Address.country }
address.state { Faker::Address.state }
address.city { Faker::Address.city }
address.street { Faker::Address.street_name }
end
end
But I have this error:
1) CommentsController POST create create a new comment with valid
attributes
Failure/Error: post :create, comment: comment_attr
ActionController::UrlGenerationError:
No route matches {:comment=>{:body=>"This is a some comment ^_^", :user=>"5", :hotel=>"1"}, :controller=>"comments",
:action=>"create"}
# ./spec/controllers/comments_controller_spec.rb:10:in block (4 levels) in <top (required)>'
# ./spec/controllers/comments_controller_spec.rb:8:inblock (3 levels) in '
2) CommentsController POST create redirects to the new comment
Failure/Error: post :create, comment: comment_attr
ActionController::UrlGenerationError:
No route matches {:comment=>{:body=>"This is a some comment ^_^", :user=>"5", :hotel=>"1"}, :controller=>"comments",
:action=>"create"}
# ./spec/controllers/comments_controller_spec.rb:16:in `block (3 levels) in '
I think your main problem is that you send to action already created object
post :create, comment: Comment.create(comment_attr)
Rails parses params and fetches id of record and trying to find member route.
It looks like you would like to test that a new Comment will be created from params in controller. And it is nested resource so you should send hotel_id as well
Try to send
it 'creates a new comment with valid attributes' do
comment_attr = attributes_for(:comment)
hotel = Hotel.last || create(:hotel)
expect{
post :create, comment: comment_attr, hotel_id: hotel.id
}.to change(Comment,:count).by(1)
end
Another thing which confuses me is line post 'comment', only: :create in routes. I have never seen option only for post - usually it is used for resources.