I am testing an Invoice model (a Client has many invoices, an Invoice belongs to a Client) and trying to check whether the create method works.
This is what I have come up with:
before do
#valid_invoice = FactoryGirl.create(:invoice)
#valid_client = #valid_invoice.client
end
it "creates a new Invoice" do
expect {
post :create, { invoice: #valid_client.invoices.build(valid_attributes), client_id: #valid_client.to_param }
}.to change(Invoice, :count).by(1)
end
This is my invoice factory:
FactoryGirl.define do
factory :invoice do
association :client
gross_amount 3.14
net_amount 3.14
number "MyString"
payment_on "2013-01-01"
vat_rate 0.19
end
end
This is the create method in the invoices_controller:
def create
#client = Client.find(params[:client_id])
#invoice = #client.invoices.build(params[:invoice])
respond_to do |format|
if #invoice.save
format.html { redirect_to([#invoice.client, #invoice], :notice => 'Invoice was successfully created.') }
format.json { render :json => #invoice, :status => :created, :location => [#invoice.client, #invoice] }
else
format.html { render :action => "new" }
format.json { render :json => #invoice.errors, :status => :unprocessable_entity }
end
end
end
And these are the valid attributes, ie the attributes needed for an invoice to be created successfully:
def valid_attributes
{
gross_amount: 3.14,
net_amount: 3.14,
number: "MyString",
payment_on: "2013-01-01",
vat_rate: 0.19
}
end
These are all valid. Maybe the client_id is missing?
It is only telling me that the count did not change by one - so I am not sure what the problem is. What am I doing wrong?
#gregates - Your answer was right, why did you remove it? :-) Post it again and I will check it as best answer.
This is the solution:
post :create, { invoice: valid_attributes, client_id: #valid_client.to_param }, valid_session
instead of
post :create, { invoice: #valid_client.invoices.build(valid_attributes), client_id: #valid_client.to_param }
in the test.
Also, I had to change the number in the valid_attributes. Debugging every single validation showed me that it was the same as in the factory - but must instead be unique. This solved it for me! Thanks for everyone's help!
Related
I'm new to Rails and I might be missing something very basic here:
User can create contact for both branches and division of a company
Branch.rb
class Branch < ApplicationRecord
belongs_to :company
has_many :contacts
end
Division.rb
class Division < ApplicationRecord
belongs_to :company
has_many :contacts
end
Contact.rb
class Contact < ApplicationRecord
belongs_to :branch
belongs_to :division
end
Now a user can create a contact from the branch page where there is no division_id and can create a contact from the Division page.
I have defined my routes.rb like this:
Routes.rb
resources :companies, :shallow => true do
get 'company_page'
resources :branches, :shallow => true do
get 'branch_page'
resources :contacts
end
resources :divisions, :shallow => true do
get 'division_page'
resources :contacts
end
end
As a result, if I create a contact from either Branch or Division, it goes to contacts#create method.
In my contacts_controller.rb, I have:
def create
#newContact = Contact.new(contact_params)
id = #division = #branch = nil
isBranch = false
if params[:branch_id] != nil
isBranch = true
id = params[:branch_id]
else
isBranch = false
id = params[:division_id]
end
if isBranch
branch = Branch.find(id)
#newContact.branch = branch
#branch = branch
else
division = Division.find(id)
#newContact.division = division
#division = division
end
respond_to do |format|
if #newContact.save
format.js
format.html { render :nothing => true, :notice => 'Contact created successfully!' }
format.json { render json: #newContact, status: :created, location: #newContact }
else
format.html { render action: "new" }
format.json { render json: #newContact, status: :unprocessable_entity }
end
end
end
But I have facing ActiveRecord Error during #newContact.save.
I'm sure I am doing something fundamentally very wrong here and Rails handles such things in another elegant way which I don't know of.
As #Anthony noted, you'll need to make your belongs_to associations optional:
# app/models/contact.rb
class Contact < ApplicationRecord
belongs_to :branch, optional: true
belongs_to :division, optional: true
end
But another problem is that params[:division_id] and params[:branch_id] are always nil. Both of those keys exist inside the [:contact] key. So the error you are getting should be ActiveRecord::RecordNotFound: Couldn't find Division with 'id'=
All that conditional logic is unnecessary. You can just make a new contact with whatever params are given. Also, you should be using Ruby convention for variable naming, which is snake_case instead of camelCase.
Finally, I assume you'll want to redirect HTML requests to either the branch or the division show page, depending on which was associated. So I've added logic to do that.
Here's a quick refactoring of the controller #create action:
def create
#new_contact = Contact.new(contact_params)
if #new_contact.save
branch = #new_contact.branch
division = #new_contact.division
redirect_path = branch ? branch_path(branch) : division_path(division)
respond_to do |format|
format.js
format.html { redirect_to redirect_path, :notice => 'Contact created successfully!' }
format.json { render json: #new_contact, status: :created, location: #new_contact }
end
else
respond_to do |format|
format.html { render action: "new" }
format.json { render json: #new_contact, status: :unprocessable_entity }
end
end
end
This proves that it works:
# spec/controllers/contacts_controller_spec.rb
require 'rails_helper'
RSpec.describe ContactsController, type: :controller do
let(:company) { Company.create!(name: 'Company Name') }
let(:division) { Division.create!(name: 'Division Name', company: company) }
let(:branch) { Branch.create!(name: 'Branch Name', company: company) }
describe '#create' do
context 'when created with a division id' do
let(:attributes) { {'division_id' => division.id, 'name' => 'Contact Name'} }
it 'creates a contact record and associates it with the division' do
expect(Contact.count).to eq(0)
post :create, params: {contact: attributes}
expect(Contact.count).to eq(1)
contact = Contact.first
expect(contact.division).to eq(division)
end
end
context 'when created with a branch id' do
let(:attributes) { {'branch_id' => branch.id, 'name' => 'Contact Name'} }
it 'creates a contact record and associates it with the branch' do
expect(Contact.count).to eq(0)
post :create, params: {contact: attributes}
expect(Contact.count).to eq(1)
contact = Contact.first
expect(contact.branch).to eq(branch)
end
end
end
end
I am having an issue with foreign keys being permitted in my new Rails 4 application.
Lets say you have a user create form, where you can create a user and assign a user type though a dropdown.
The user model will then have a foreign key: user_type_id.
I have a RSpec test which uses FactoryGirl, and if I debug and look at params the user_type has the value 2, but if my permit params look like this:
private
def user_params
params.require(:user).permit(:name, :password, :user_type_id)
end
I won't get any user_type out. I have also tried with:
private
def user_params
params.require(:user).permit(:name, :password, user_type_attributes: [:user_type_id])
end
but without any luck.
What is the way to permit it in my post action in my User controller?
Update:
I don't have a UI yet, I try to do this the TDD way, so basically it is my RSpec-tests which fails.
The create user action looks like this:
def create
#user = User.new(user_params)
authorize #user
respond_to do |format|
if #user.save
format.html { render json: #user, status: :created, location: #user }
format.json { render json: #user, status: :created, location: #user }
else
format.html { render json: #user, status: :unprocessable_entity, location: #user }
format.json { render json: #user.errors, status: :unprocessable_entity }
end
end
end
and the RSpec test looks like this:
it 'should create a user' do
expect {
post :create, { :user => FactoryGirl.attributes_for(:User) }
}.to change(User, :count).by(1)
end
The FactoryGirl for my user looks like this:
FactoryGirl.define do
factory :User do |f|
f.email { Faker::Internet.email }
f.password { '12345678' }
f.password_confirmation { '12345678' }
f.user_type { FactoryGirl.create(:UserType) }
end
end
If I debug my #user object it doesn't have a user_type added, but if I debug the params object, it does, contain a user_type: 2
Any ideas?
You are not creating an Id in the Factory. You are creating the new associated object. You will have to retrieve the ID in the params passed to the controller.
I suggest:
FactoryGirl.define do
factory :User do |f|
f.email { Faker::Internet.email }
f.password { '12345678' }
f.password_confirmation { '12345678' }
f.user_type 999
end
end
In your spec:
before(:each) do
type = FactoryGirl.create(:UserType, id: 999)
end
And then:
it 'should create a user' do
expect { :create, { :user => FactoryGirl.attributes_for(:User)}
}.to change(User, :count).by(1)
end
And remove the association from the FactoryGirl action.
EDIT:
If the user type is mandatory, and assuming that you have them in the database, you just need to insert in the factory the user_id you want for that user type. And you won't need to merge the params after.
I'm now making Rspec test for create action of ItemsController, however I'm in trouble about the error. (There are models as Item, Auction, Bid, User)
The create method is as follow
# POST /items
# POST /items.json
def create
#item = Item.new(params[:item])
#item.user_id = current_user.id
respond_to do |format|
if #item.save
format.html { redirect_to #item, notice: 'Item was successfully created.' }
format.json { render json: #item, status: :created, location: #item }
else
format.html { render action: "new" }
format.json { render json: #item.errors, status: :unprocessable_entity }
end
end
#elapsed_seconds =(((params[:item][:days].to_i * 24)+params[:item][:hours].to_i)*60+params[:item][:minutes].to_i)*60
#auction = Auction.create(:item_id => #item.id, :highest_bid_id => 0, :exp_time =>
#item.created_at+ #elapsed_seconds.seconds, :suspend => 0, :user_id => #current_user.id, :extend_bit => 0 )
#item.update_attributes(:auction_id => #auction.id)
#item_id = #item.id
#auction_id = #auction.id
#t1 = Thread.new{
#timer = Timers.new
#bid_timer = #timer.after(#elapsed_seconds){
if Auction.find_by_id(#auction_id).suspend != 1
buy
end
}
#timer.wait
}
end
def buy
if Auction.find_by_id(#auction_id).extend_bit == 1
extendtimer
else
if Auction.find_by_id(#auction_id).highest_bid_id != 0
Item.find_by_auction_id(#auction_id).update_attributes(:sold => 1, :sold_to => Bid.find_by_id(Auction.find_by_id(#auction_id).highest_bid_id).user_id )
MyMailer.auction_winner_email(Auction.find_by_id(#auction_id)).deliver
else
Item.find_by_auction_id(#auction_id).update_attributes(:sold => 0, :sold_to => 0 )
MyMailer.no_bids_email(Auction.find_by_id(#auction_id)).deliver
end
#t1.join
end
end
def extendtimer
Auction.find_by_id(#auction_id).update_attributes(:extend_bit => 0)
#exp = Auction.find_by_id(#auction_id).exp_time + 2.minutes
Auction.find_by_id(#auction_id).update_attributes(:exp_time => #exp)
#min = Item.find_by_id(#item_id).minutes + 2
Item.find_by_id(#item_id).update_attributes(:minutes => #min)
#timer2 = Timers.new
#extend_timer = #timer2.after(120){ buy }
#timer2.wait
end
And my Rspec is as follow. This test intends that at first objects were made to use (such as current_user, auction, "item). Then also stubs are declared. (such asAuction.stub(:find_by_id).and_return(auction)`)
require 'spec_helper'
describe ItemsController do
let(:valid_session) { {} }
describe "POST create" do
it "" do
#declare the objects and stubs
current_user = User.new(id:'1')
current_user.save
auction = Auction.new(id:'1',highest_bid_id:'1', extend_bit:'1')
auction.save
Auction.stub(:find_by_id).and_return(auction)
bid = Bid.new(auction_id:'1',amount:'150.0')
bid.save
Bid.stub(:find_by_id).and_return(bid)
item = Item.new(id:'1',auction_id:'1',min_bid_price:'100.0')
item.save
Item.stub(:find_by_id).and_return(item)
#POST create
post :create, {:item => {'id' => '2','days'=>'1','hours'=>'1','minutes'=>'1'}}
response.should redirect_to(#item)
flash[:notice].should eql("Item was successfully created.")
end
end
end
Unfortunately I ran Rspec, I got the error as follow. I think #item in controller could not be successfully created by #item = Item.new(params[:item]), so this error might be happen. However I couldn't find how to fix. I already used many hours for this. I would like to have someone's help.
Failures:
1) ItemsController POST create
Failure/Error: post :create, {:item => {'id' => '2','days'=>'1','hours'=>'1','minutes'=>'1'}}
RuntimeError:
Called id for nil, which would mistakenly be 4 -- if you really wanted the id of nil, use object_id
# ./app/controllers/items_controller.rb:72:in `create'
# ./spec/controllers/items_controller_spec.rb:19:in `block (3 levels) in <top (required)>'
Finished in 0.637 seconds 1 example, 1 failure
Failed examples:
rspec ./spec/controllers/items_controller_spec.rb:11 # ItemsController POST create
Randomized with seed 33483
Since no one is logged in, current_user method returns nil. Setting local variable current_user in spec won't help for that. Instead, you could define ApplicationController#current_user= method:
def current_user=(user)
#current_user ||= user
end
and call it in your specs:
controller.current_user = current_user
I asked about my Rspec test as follow.
Rspec - RuntimeError: Called id for nil, which would mistakenly be 4
On the same code (Rspec test for "items_controller.rb"), I am trying to make the test for "PUT update". However I got the error "Paperclip::AdapterRegistry::NoHandlerError: No handler found for "#".
My Rspec test is as follow. Honestly, I guess that the cause of this fail is ""photo" => File.new(Rails.root + 'app/assets/images/rails.png')" on "let(:valid_attributes)". However, I tried several ways but I couldn't fix.
By the way, my rails version is "Rails 3.2.14". Then I tried following post, but also couldn't.
Can't figure out what's causing my tests to fail
The error is as follows.
......F....
Failures:
1) ItemsController PUT update could not update successfully
Failure/Error: put :update, {:id => item.to_param, :item => valid_attributes}, valid_session
Paperclip::AdapterRegistry::NoHandlerError:
No handler found for "#<File:0x5d4c548>"
# ./app/controllers/items_controller.rb:110:in `block in update'
# ./app/controllers/items_controller.rb:108:in `update'
# ./spec/controllers/items_controller_spec.rb:95:in `block (3 levels) in <top (required)>'
Finished in 1.75 seconds
11 examples, 1 failure
Failed examples:
rspec ./spec/controllers/items_controller_spec.rb:91 # ItemsController PUT update could not update successfully
Randomized with seed 40912
My Rspec test is as follows.
require 'spec_helper'
require 'date'
describe ItemsController do
let(:valid_attributes) { {
"days" => "1",
"hours" => "1",
"minutes" => "1",
"name"=>"HogeHoge" ,
"category" => "Gift",
"min_bid_price" => "100.0",
"description" => "HogeHoge",
"photo" => File.new(Rails.root + 'app/assets/images/rails.png')
} }
let(:valid_session) { {} }
it "returns http success" do
get "index"
response.should be_success
end
it "returns http success" do
get "new"
response.should be_success
end
describe "POST create" do
it "" do
#declare the objects and stubs
current_user = User.new(id:'1')
current_user.save
#"current_user=(user)" function on controller
controller.current_user = current_user
#auction
auction = Auction.new(id:'1',highest_bid_id:'1', extend_bit:'1')
auction.save
Auction.stub(:find_by_id).and_return(auction)
#bid
bid = Bid.new(auction_id:'1',amount:'150.0')
bid.save
Bid.stub(:find_by_id).and_return(bid)
#item
item = Item.new(id:'1',auction_id:'1',min_bid_price:'100.0')
item.save
Item.stub(:find_by_id).and_return(item)
date = DateTime.now
post :create, {:item => {'id' => '2','days'=>'1','hours'=>'1','minutes'=>'1','created_at'=>date}}
response.should be_success
end
end
describe "GET index" do
it "assigns all items as #items" do
item = Item.create! valid_attributes
get :index, {}, valid_session
assigns(:items).should eq([item])
end
end
describe "GET show" do
it "assigns the requested item as #item" do
item = Item.create! valid_attributes
get :show, {:id => item.to_param}, valid_session
assigns(:item).should eq(item)
end
end
describe "GET new" do
it "assigns a new item as #item" do
get :new, {}, valid_session
assigns(:item).should be_a_new(Item)
end
end
describe "GET edit" do
it "assigns the requested item as #item" do
item = Item.create! valid_attributes
get :edit, {:id => item.to_param}, valid_session
assigns(:item).should eq(item)
end
end
describe "PUT update" do
it "could not update successfully" do
item = Item.create! valid_attributes
# Trigger the behavior that occurs when invalid params are submitted
Item.any_instance.stub(:save).and_return(false)
put :update, {:id => item.to_param, :item => valid_attributes}, valid_session
assigns(:item).should eq(item)
response.should redirect_to(#item)
end
it "could not update successfully" do
item = Item.create! valid_attributes
# Trigger the behavior that occurs when invalid params are submitted
Item.any_instance.stub(:save).and_return(false)
put :update, {:id => item.to_param, :item => { }}, valid_session
response.should render_template("edit")
end
end
describe "DELETE destroy" do
it "destroys the requested item" do
item = Item.create! valid_attributes
expect {
delete :destroy, {:id => item.to_param}, valid_session
}.to change(Item, :count).by(-1)
end
it "redirects to the items list" do
item = Item.create! valid_attributes
delete :destroy, {:id => item.to_param}, valid_session
response.should redirect_to(items_url)
end
end
end
My "items_controller.rb" is as follw.
require 'timers'
class ItemsController < ApplicationController
#instance of current user
def current_user=(user)
#current_user ||= user
end
def extendtimer
Auction.find_by_id(#auction_id).update_attributes(:extend_bit => 0)
#exp = Auction.find_by_id(#auction_id).exp_time + 2.minutes
Auction.find_by_id(#auction_id).update_attributes(:exp_time => #exp)
#min = Item.find_by_id(#item_id).minutes + 2
Item.find_by_id(#item_id).update_attributes(:minutes => #min)
#timer2 = Timers.new
#extend_timer = #timer2.after(120){ buy }
#timer2.wait
end
def buy
if Auction.find_by_id(#auction_id).extend_bit == 1
extendtimer
else
if Auction.find_by_id(#auction_id).highest_bid_id != 0
Item.find_by_auction_id(#auction_id).update_attributes(:sold => 1, :sold_to => Bid.find_by_id(Auction.find_by_id(#auction_id).highest_bid_id).user_id )
MyMailer.auction_winner_email(Auction.find_by_id(#auction_id)).deliver
else
Item.find_by_auction_id(#auction_id).update_attributes(:sold => 0, :sold_to => 0 )
MyMailer.no_bids_email(Auction.find_by_id(#auction_id)).deliver
end
#t1.join
end
end
def index
#items = Item.all
respond_to do |format|
format.html # index.html.erb
format.json { render json: #items }
end
end
# GET /items/1
# GET /items/1.json
def show
#item = Item.find(params[:id])
respond_to do |format|
#format.html # show.html.erb
format.html { render layout: (request.headers["X-Requested-With"] != 'XMLHttpRequest') }
format.json { render json: #item }
end
end
# GET /items/new
# GET /items/new.json
def new
#item = Item.new
respond_to do |format|
format.html # new.html.erb
format.json { render json: #item }
end
end
# GET /items/1/edit
def edit
#item = Item.find(params[:id])
end
# POST /items
# POST /items.json
def create
#item = Item.new(params[:item])
#item.user_id = current_user.id
respond_to do |format|
if #item.save
format.html { redirect_to #item, notice: 'Item was successfully created.' }
format.json { render json: #item, status: :created, location: #item }
else
format.html { render action: "new" }
format.json { render json: #item.errors, status: :unprocessable_entity }
end
end
#elapsed_seconds =(((params[:item][:days].to_i * 24)+params[:item][:hours].to_i)*60+params[:item][:minutes].to_i)*60
#auction = Auction.create(:item_id => #item.id, :highest_bid_id => 0, :exp_time => #item.created_at+ #elapsed_seconds.seconds, :suspend => 0, :user_id => #current_user.id, :extend_bit => 0 )
#item.update_attributes(:auction_id => #auction.id)
#item_id = #item.id
#auction_id = #auction.id
#t1 = Thread.new{
#timer = Timers.new
#bid_timer = #timer.after(#elapsed_seconds){
if Auction.find_by_id(#auction_id).suspend != 1
buy
end
}
#timer.wait
}
end
# PUT /items/1
# PUT /items/1.json
def update
#item = Item.find(params[:id])
respond_to do |format|
if #item.update_attributes(params[:item])
format.html { redirect_to #item, notice: 'Item was successfully updated.' }
format.json { head :no_content }
else
format.html { render action: "edit" }
format.json { render json: #item.errors, status: :unprocessable_entity }
end
end
end
# DELETE /items/1
# DELETE /items/1.json
def destroy
#item = Item.find(params[:id])
#item.destroy
respond_to do |format|
format.html { redirect_to items_url }
format.json { head :no_content }
end
end
end
I would like to have someone's advice. Thank you in advance.
Try using Rack::Test::UploadedFile instead of File.new
require 'rack/test'
Rack::Test::UploadedFile.new('fixtures/test_file.png', 'image/png')
im pretty sure your problem is with the form_for in the view,
try something like this:
<%= form_for #restaurante, :html => { :multipart => true } do |form| %>
Nome:<%= form.text_field :nome%>
Endereço:<%= form.text_field :endereco %>
Especialidade:<%= form.text_field :especialidade %>
Foto:<%= form.file_field :foto %>
<%= form.submit 'create'%>
<% end %>
Make sure it's multipart/form-data in the test.
I have a helper method for this.
module Support
module Acceptance
module ClassMethods
def multipart_form_data!
header 'Accept', 'application/json'
header 'Content-Type', 'multipart/form-data'
end
end
end
end
I am setting a user through modifying the params instead of creating a hidden_field in the form. As far as I understand, this is a more secure way of handling mass-assignment.
def update
#exercise = Exercise.find(params[:id])
#this is the important part
if params[:exercise][:log_entries_attributes].present?
params[:exercise][:log_entries_attributes].each do |value|
value[1].merge!(:user_id => current_user.id)
end
end
#end the important part
respond_to do |format|
if #exercise.update_attributes(params[:exercise])
format.html { redirect_to_back_or_default #exercise, notice: "Exercise was successfully updated." }
format.mobile { redirect_to #exercise, notice: 'Exercise was successfully updated.' }
format.json { head :ok }
else
format.html { render action: "edit" }
format.mobile { redirect_to #exercise, notice: "#{#exercise.errors.full_messages.to_sentence}" }
format.json { render json: #exercise.errors, status: :unprocessable_entity }
end
end
end
In my spec I have the following:
describe "with log_entry_attributes" do
it "updates log_entries_attributes and sets user" do
exercise = FactoryGirl.create(:exercise)
log_entry = FactoryGirl.build(:log_entry)
exercise.log_entries << log_entry
exercise.save
controller.stub(:current_user).and_return(#user)
put :update, :id => exercise.id, :exercise => FactoryGirl.build(:exercise, "log_entries_attributes" => {":0" => {"reps" => "5", "weight" => "5"}}).attributes.symbolize_keys
assigns(:exercise).log_entries.first.user.should eq(#user)
end
end
I get undefined method user for nil:NilClass. I think I know why I get undefined method user. There's just no way to get the association through assigns. I'm not sure how to test that the user_id is being set properly through the current_user. Any help?
Work with mocked object:
exercise = double "exercise"
Exercise.should_receive(:find).and_return(exercise)
and test with:
exercise.should_receive(:update_attributes).with(correct_params)