My program functions as expected, but there are problems in my RSpec controller test code. I need help troubleshooting the specs, which were created when I ran the scaffold generator. There are three "POST create with valid params" failures:
POST create
with valid params
creates a new Appointment (FAILED - 1)
assigns a newly created appointment as #appointment (FAILED - 2)
redirects to the created appointment (FAILED - 3)
Failures:
1) AppointmentsController POST create with valid params creates a new Appointment
Failure/Error: expect {
expected #count to have changed by 1, but was changed by 0
# ./spec/controllers/appointments_controller_spec.rb:90:in `block (4 levels) in <top (required)>'
2) AppointmentsController POST create with valid params assigns a newly created appointment as #appointment
Failure/Error: expect(assigns(:appointment)).to be_persisted
expected `#<Appointment id: nil, member_id: nil, trainer_id: nil, created_at: nil, updated_at: nil, date: "2020-01-02", starts_at: "2000-01-01 08:00:00", ends_at: "2000-01-01 09:00:00">.persisted?` to return true, got false
# ./spec/controllers/appointments_controller_spec.rb:99:in `block (4 levels) in <top (required)>'
3) AppointmentsController POST create with valid params redirects to the created appointment
Failure/Error: expect(response).to redirect_to(Appointment.last)
Expected response to be a <redirect>, but was <200>
# ./spec/controllers/appointments_controller_spec.rb:104:in `block (4 levels) in <top (required)>'
In the second failure, I notice there are nil values for appointment id, member and trainer, although I have valid factories for member and trainer. Tests for my member and trainer factories pass, and they work as expected. I assume the problem must be caused by the way I set up the "valid attributes" hash in the Controller spec, but I don't know what's wrong. Why are the POST create tests failing? What do I need to do to get them to pass?
Here's the code for the Appointments Controller RSpec:
require 'rails_helper'
RSpec.describe AppointmentsController, :type => :controller do
let(:valid_attributes) { {
'date' => '2020-01-02',
'starts_at' => '08:00:00',
'ends_at' => '09:00:00',
'member' => FactoryGirl.build(:member),
'trainer' => FactoryGirl.build(:trainer)
}
}
let(:invalid_attributes) { {
'date' => '2000-01-02',
'starts_at' => '06:00:00',
'ends_at' => '09:00:00',
'member' => FactoryGirl.build(:member),
'trainer' => FactoryGirl.build(:trainer)
}
}
let(:valid_session) { {
'date' => '2020-12-30',
'starts_at' => '15:00:00',
'ends_at' => '17:00:00',
'member' => FactoryGirl.build(:member),
'trainer' => FactoryGirl.build(:trainer)
}
}
describe "GET index" do
it "assigns all appointments as #appointments" do
appointment = Appointment.create! valid_attributes
get :index, {}, valid_session
expect(assigns(:appointments)).to eq([appointment])
end
end
describe "GET show" do
it "assigns the requested appointment as #appointment" do
appointment = Appointment.create! valid_attributes
get :show, {:id => appointment.to_param}, valid_session
expect(assigns(:appointment)).to eq(appointment)
end
end
describe "GET new" do
it "assigns a new appointment as #appointment" do
get :new, {}, valid_session
expect(assigns(:appointment)).to be_a_new(Appointment)
end
end
describe "GET edit" do
it "assigns the requested appointment as #appointment" do
appointment = Appointment.create! valid_attributes
get :edit, {:id => appointment.to_param}, valid_session
expect(assigns(:appointment)).to eq(appointment)
end
end
describe "POST create" do
describe "with valid params" do
it "creates a new Appointment" do
expect {
post :create, {:appointment => valid_attributes}, valid_session
}.to change(Appointment, :count).by(1)
save_and_open_page
end
it "assigns a newly created appointment as #appointment" do
post :create, {:appointment => valid_attributes}, valid_session
expect(assigns(:appointment)).to be_a(Appointment)
expect(assigns(:appointment)).to be_persisted
end
it "redirects to the created appointment" do
post :create, {:appointment => valid_attributes}, valid_session
expect(response).to redirect_to(Appointment.last)
end
end
describe "with invalid params" do
it "assigns a newly created but unsaved appointment as #appointment" do
post :create, {:appointment => invalid_attributes}, valid_session
expect(assigns(:appointment)).to be_a_new(Appointment)
end
it "re-renders the 'new' template" do
post :create, {:appointment => invalid_attributes}, valid_session
expect(response).to render_template("new")
end
end
end
describe "PUT update" do
describe "with valid params" do
let(:new_attributes) { {
'date' => '2020-01-02',
'starts_at' => '10:00:00',
'ends_at' => '12:00:00',
'member' => FactoryGirl.build(:member),
'trainer' => FactoryGirl.build(:trainer)
}
}
let(:invalid_attributes) { {
'date' => '2005-03-15',
'starts_at' => '04:00:00',
'ends_at' => '09:00:00',
'member' => FactoryGirl.build(:member),
'trainer' => FactoryGirl.build(:trainer)
}
}
it "updates the requested appointment" do
appointment = Appointment.create! valid_attributes
put :update, {:id => appointment.to_param, :appointment => new_attributes}, valid_session
appointment.reload
expect(controller.notice).to eq('Appointment was successfully updated.')
end
it "assigns the requested appointment as #appointment" do
appointment = Appointment.create! valid_attributes
put :update, {:id => appointment.to_param, :appointment => valid_attributes}, valid_session
expect(assigns(:appointment)).to eq(appointment)
end
it "redirects to the appointment" do
appointment = Appointment.create! valid_attributes
put :update, {:id => appointment.to_param, :appointment => valid_attributes}, valid_session
expect(response).to redirect_to(appointment)
end
end
describe "with invalid params" do
it "assigns the appointment as #appointment" do
appointment = Appointment.create! valid_attributes
put :update, {:id => appointment.to_param, :appointment => invalid_attributes}, valid_session
expect(assigns(:appointment)).to eq(appointment)
end
it "re-renders the 'edit' template" do
appointment = Appointment.create! valid_attributes
put :update, {:id => appointment.to_param, :appointment => invalid_attributes}, valid_session
expect(response).to render_template("edit")
end
end
end
describe "DELETE destroy" do
it "destroys the requested appointment" do
appointment = Appointment.create! valid_attributes
expect {
delete :destroy, {:id => appointment.to_param}, valid_session
}.to change(Appointment, :count).by(-1)
end
it "redirects to the appointments list" do
appointment = Appointment.create! valid_attributes
delete :destroy, {:id => appointment.to_param}, valid_session
expect(response).to redirect_to(appointments_url)
end
end
end
Here's the code for the Appointments Controller:
class AppointmentsController < ApplicationController
before_action :set_appointment, only: [:show, :edit, :update, :destroy]
# GET /appointments
# GET /appointments.json
def index
#appointments = Appointment.all
end
# GET /appointments/1
# GET /appointments/1.json
def show
end
# GET /appointments/new
def new
#appointment = Appointment.new
end
# GET /appointments/1/edit
def edit
end
# POST /appointments
# POST /appointments.json
def create
#appointment = Appointment.new(appointment_params)
respond_to do |format|
if #appointment.save
format.html { redirect_to #appointment, notice: 'Appointment was successfully created.' }
format.json { render :show, status: :created, location: #appointment }
else
format.html { render :new }
format.json { render json: #appointment.errors, status: :unprocessable_entity }
end
end
end
# PATCH/PUT /appointments/1
# PATCH/PUT /appointments/1.json
def update
respond_to do |format|
if #appointment.update(appointment_params)
format.html { redirect_to #appointment, notice: 'Appointment was successfully updated.' }
format.json { render :show, status: :ok, location: #appointment }
else
format.html { render :edit }
format.json { render json: #appointment.errors, status: :unprocessable_entity }
end
end
end
# DELETE /appointments/1
# DELETE /appointments/1.json
def destroy
#appointment.destroy
respond_to do |format|
format.html { redirect_to appointments_url, notice: 'Appointment was successfully destroyed.' }
format.json { head :no_content }
end
end
private
# Use callbacks to share common setup or constraints between actions.
def set_appointment
#appointment = Appointment.find(params[:id])
end
# Never trust parameters from the scary internet, only allow the white list through.
def appointment_params
params.require(:appointment).permit(:date, :starts_at, :ends_at, :member_id, :trainer_id)
end
end
The code for the Appointment Model:
class Appointment < ActiveRecord::Base
belongs_to :member
belongs_to :trainer
validates_date :date, :on => :create, :on_or_after => :today
validates_time :starts_at, :between => ['06:30', '21:00']
validates_time :starts_at, :after => :now, :if => :today_appointment #scopes validation to current day only
validates_time :ends_at, :after => :starts_at
validate :duration_of_appointment
validates :member, :trainer, presence: true
validates :starts_at, :ends_at, :overlap => {
:exclude_edges => ["starts_at", "ends_at"],
:scope => "date",
:scope => "starts_at",
:scope => "trainer_id"
}
validates :starts_at, :ends_at, :overlap => {
:exclude_edges => ["starts_at", "ends_at"],
:scope => "member_id"
}
private
def today_appointment
Date.current == self.date
end
def duration_of_appointment
length = (ends_at - starts_at) / 60
return if length.between?(30, 120) # stops validation if length falls between the two integers
errors.add(:base, 'Duration must be between 30 and 120 minutes')
end
end
You shouldn't provide a built instance of Member and Trainer to controller. Instead, create Member and Trainer, and pass their ids as member_id,
trainer_id in valid_attributes hash.
First, create needed trainer and member:
before :each do
#trainer = FactoryGirl.build(:trainer)
#member = FactoryGirl.build(:member)
end
Then use their ids in your hash:
let(:valid_attributes) { {
'date' => '2020-01-02',
'starts_at' => '08:00:00',
'ends_at' => '09:00:00',
'member_id' => #member.id,
'trainer_id' => #trainer.id
}
In your code, you didn't even create them, you've only built them, but only changing build(:trainer) to create(:trainer) won't work in your case.
Related
I am working on a reminder scheduler and used rails scaffold to create the models and views for the reminder. So far I have the controller looks like this:
class RemindersController < ApplicationController
before_action :set_reminder, only: [:show, :edit, :update, :destroy]
##reminder_hash = {}
##scheduler = Rufus::Scheduler.singleton
def index
#reminders = Reminder.all
end
def new
#reminder = Reminder.new
end
def edit
end
def create
#reminder = Reminder.new(reminder_params)
respond_to do |format|
if #reminder.save
format.html { redirect_to reminders_url, notice: I18n.t('Reminder was successfully created.') }
format.json { render :show, status: :created, location: #reminder }
schedule_reminder #reminder.id, #reminder.reminder_day, #reminder.reminder_time
else
format.html { render :new }
format.json { render json: #reminder.errors, status: :unprocessable_entity }
end
end
end
def update
respond_to do |format|
if #reminder.update(reminder_params)
# Unschedule the job.
jobs = ##scheduler.jobs(tag: #reminder.id)
jobs.each { |job| ##scheduler.unschedule(job) }
# Reschedule the job at the new time.
schedule_reminder #reminder.id, #reminder.reminder_day, #reminder.reminder_time
format.html { redirect_to reminders_url, notice: I18n.t('Reminder was successfully updated.') }
format.json { render :show, status: :ok, location: #reminder }
else
format.html { render :edit }
format.json { render json: #reminder.errors, status: :unprocessable_entity }
end
end
end
def destroy
#reminder.destroy
respond_to do |format|
jobs = ##scheduler.jobs(tag: #reminder.id)
jobs.each { |job| ##scheduler.unschedule(job) }
format.html { redirect_to reminders_url, notice: I18n.t('Reminder was successfully destroyed.') }
format.json { head :no_content }
end
end
# Internal: Schedule reminder to be sent at appropriate time.
#
# id - The Reminder id used to uniquely identify the job associated with that reminder.
# reminder day - The day when the reminder will be sent.
# reminder time - The time when the reminder will be sent.
#
# Returns nothing.
def schedule_reminder(tag, reminder_day, reminder_time)
minute = reminder_time.strftime('%M')
hour = reminder_time.strftime('%H')
day = reminder_day[0...3].upcase
cron_string = '0 ' + minute + ' ' + hour + ' * * ' + day
##scheduler.cron cron_string, tag: tag do
EmailController.mentor_reminder_email_sender
end
end
private
# Use callbacks to share common setup or constraints between actions.
def set_reminder
#reminder = Reminder.find(params[:id])
end
# Whitelist the paramters.
def reminder_params
params.fetch(:reminder, {}).permit(:reminder_day, :reminder_time)
end
end
The spec looks like this:
require 'rails_helper'
RSpec.describe RemindersController, type: :controller do
let(:valid_attributes) {
{ id: 1, reminder_day: :monday, reminder_time: Time.now}
}
let(:invalid_attributes) {
{ id: nil, reminder_day: nil, reminder_time: nil}
}
let(:valid_session) { {} }
describe 'DELETE #destroy' do
it 'destroys the requested reminder' do
reminder = Reminder.create! valid_attributes
expect {
delete :destroy, {id: reminder.to_param}, valid_session
}.to change(Reminder, :count).by(-1)
end
it 'does not call reminder scheduler function' do
post :create, {reminder: valid_attributes}, valid_session
expect(controller).to_not receive(:schedule_reminder)
end
it 'redirects to the reminders list' do
reminder = Reminder.create! valid_attributes
delete :destroy, {id: reminder.to_param}, valid_session
expect(response).to redirect_to(reminders_path)
end
end
describe '#create' do
let(:reminder) { Reminder.new(id: 1, reminder_day: 1, reminder_time: Time.now) }
let(:reminders_controller) { RemindersController.new }
context 'with valid params' do
before do
allow(controller).to receive(:schedule_reminder)
end
it 'creates a new Reminder' do
expect {
post :create, {reminder: valid_attributes}, valid_session
}.to change(Reminder, :count).by(1)
end
it 'assigns a newly created reminder as #reminder' do
post :create, {reminder: valid_attributes}, valid_session
expect(assigns(:reminder)).to be_a(Reminder)
expect(assigns(:reminder)).to be_persisted
end
it 'calls schedule_reminder with correct parameters' do
expect(reminders_controller).to receive(:schedule_reminder)
.with(reminder.id, reminder.reminder_day, reminder.reminder_time)
reminders_controller.schedule_reminder(reminder.id, reminder.reminder_day, reminder.reminder_time)
end
end
context 'with invalid params' do
it 'assigns a newly created but unsaved reminder as #reminder' do
post :create, {reminder: invalid_attributes}, valid_session
expect(assigns(:reminder)).to be_a_new(Reminder)
end
it 're-renders the "new" template' do
post :create, {reminder: invalid_attributes}, valid_session
expect(response).to render_template('new')
end
it 'does not call schedule_reminder' do
expect(controller).to_not receive(:schedule_reminder)
end
end
end
describe 'PUT #update' do
context 'with valid params' do
let(:new_attributes) {
{ id: 2, reminder_day: :tuesday, reminder_time: Time.now}
}
let(:reminders_controller) { RemindersController.new }
let(:reminder) { Reminder.new(id: 1, reminder_day: 1, reminder_time: Time.now) }
it 'updates the requested reminder' do
reminder = Reminder.create! valid_attributes
put :update, { id: reminder.to_param, reminder: new_attributes}, valid_session
reminder.reload
end
it 'calls reminder scheduler function with correct parameters' do
post :create, {reminder: valid_attributes}, valid_session
expect(reminders_controller).to receive(:schedule_reminder)
.with(reminder.id, reminder.reminder_day, reminder.reminder_time)
reminders_controller.schedule_reminder(reminder.id, reminder.reminder_day, reminder.reminder_time)
end
it 'assigns the requested reminder as #reminder' do
reminder = Reminder.create! valid_attributes
put :update, { id: reminder.to_param, reminder: valid_attributes}, valid_session
expect(assigns(:reminder)).to eq(reminder)
end
it 'redirects to the reminder' do
reminder = Reminder.create! valid_attributes
put :update, { id: reminder.to_param, reminder: valid_attributes}, valid_session
expect(response).to redirect_to(reminders_path)
end
end
context 'with invalid params' do
it 'assigns the reminder as #reminder' do
reminder = Reminder.create! valid_attributes
put :update, {id: reminder.to_param, reminder: invalid_attributes}, valid_session
expect(assigns(:reminder)).to eq(reminder)
end
it 're-renders the "edit" template' do
reminder = Reminder.create! valid_attributes
put :update, {id: reminder.to_param, reminder: invalid_attributes}, valid_session
expect(response).to render_template('edit')
end
it 'does not call schedule_reminder' do
expect(controller).to_not receive(:schedule_reminder)
end
end
end
describe 'GET #index' do
it 'assigns all reminders as #reminders' do
reminder = Reminder.create! valid_attributes
get :index, {}, valid_session
expect(assigns(:reminders)).to eq([reminder])
end
end
describe 'GET #new' do
it 'assigns a new reminder as #reminder' do
get :new, {}, valid_session
expect(assigns(:reminder)).to be_a_new(Reminder)
end
end
describe 'GET #edit' do
context 'when reminder is found' do
it 'assigns the requested reminder as #reminder' do
reminder = Reminder.create! valid_attributes
get :edit, {id: reminder.to_param}, valid_session
expect(assigns(:reminder)).to eq(reminder)
end
end
context 'when reminder is not found' do
it 're-renders the "edit" template' do
reminder = Reminder.create! valid_attributes
put :update, {id: reminder.to_param, reminder: invalid_attributes}, valid_session
expect(response).to render_template('edit')
end
end
end
end
I am trying to replace all the create statements with a mock/double to eliminate database calls. I have tried replacing all create statements with this:
reminder = double('Reminder', id: 1, reminder_day: :monday, reminder_time: Time.now)
and other variations.
I replaced this :
describe 'DELETE #destroy' do
it 'destroys the requested reminder' do
reminder = Reminder.create! valid_attributes
expect {
delete :destroy, {id: reminder.to_param}, valid_session
}.to change(Reminder, :count).by(-1)
end
it 'does not call reminder scheduler function' do
post :create, {reminder: valid_attributes}, valid_session
expect(controller).to_not receive(:schedule_reminder)
end
it 'redirects to the reminders list' do
reminder = Reminder.create! valid_attributes
delete :destroy, {id: reminder.to_param}, valid_session
expect(response).to redirect_to(reminders_path)
end
end
with this:
describe 'DELETE #destroy' do
let(:reminder){double('Reminder', id: 1, reminder_day: :monday, reminder_time: Time.now)}
before do
allow(Reminder).to receive(:create).and_return(reminder)
end
it 'destroys the requested reminder' do
reminder = Reminder.create
expect {
delete :destroy, {id: reminder.to_param}, valid_session
}.to change(Reminder, :count).by(-1)
end
it 'does not call reminder scheduler function' do
post :create, {reminder: valid_attributes}, valid_session
expect(controller).to_not receive(:schedule_reminder)
end
it 'redirects to the reminders list' do
reminder = Reminder.create
delete :destroy, {id: reminder.to_param}, valid_session
expect(response).to redirect_to(reminders_path)
end
end
I'm getting these errors:
Failure/Error: expect(response).to redirect_to(reminders_path)
Expected response to be a , but was <404>
Failure/Error: expect {
expected #count to have changed by -1, but was changed by 0
I'm trying to learn how to do tests in Rails. I have a foods_controller and in the test folder, my food.yml is filled with all of the parameters that should be present when creating a new food object and in foods_controller_test.rb, the parameters in "should create food" are matching the ones in food.yml. When running a test I get this error:
ArgumentError: too few arguments
app/controllers/application_controller.rb:45:in `format'
app/controllers/application_controller.rb:45:in `authorize'
test/controllers/foods_controller_test.rb:21:in `block (2 levels) in <class:FoodsControllerTest>'
test/controllers/foods_controller_test.rb:20:in `block in <class:FoodsControllerTest>
Can anyone exaplain me what is wrong here?
food.yml
one:
name: "Whatever"
portion: "100g"
calories: 1
fat: 1.5
carb: 1.5
protein: 1.5
fiber: 1.5
sugar: 1.5
category: "Grains"
two:
name: "MyString"
portion: "MyString"
calories: 1
fat: 1.5
carb: 1.5
protein: 1.5
fiber: 1.5
sugar: 1.5
category: "MyString"
foods_controller_test.rb
require 'test_helper'
class FoodsControllerTest < ActionController::TestCase
setup do
#food = foods(:one)
end
test "should get index" do
get :index
assert_response :success
assert_not_nil assigns(:foods)
end
test "should get new" do
get :new
assert_response :success
end
test "should create food" do
assert_difference('Food.count') do
post :create, food: { calories: #food.calories, carb: #food.carb, category: #food.category, fat: #food.fat, fiber: #food.fiber, name: #food.name, portion: #food.portion, protein: #food.protein, sugar: #food.sugar }
end
assert_redirected_to food_path(assigns(:food))
end
test "should show food" do
get :show, id: #food
assert_response :success
end
test "should get edit" do
get :edit, id: #food
assert_response :success
end
test "should update food" do
patch :update, id: #food, food: { calories: #food.calories, carb: #food.carb, category: #food.category, fat: #food.fat, fiber: #food.fiber, name: #food.name, portion: #food.portion, protein: #food.protein, sugar: #food.sugar }
assert_redirected_to food_path(assigns(:food))
end
test "should destroy food" do
assert_difference('Food.count', -1) do
delete :destroy, id: #food
end
assert_redirected_to foods_path
end
end
foods_controller.rb
class FoodsController < ApplicationController
before_action :set_food, only: [:show, :edit, :update, :destroy]
before_filter :authorize, only: [:create, :delete]
# GET /foods
# GET /foods.json
def index
#foods = Food.order(:name)
# #foods = #foods.sort_by &:name
# #users.sort! { |a,b| a.name.downcase <=> b.name.downcase }
#food_categories = Food::CATEGORIES.keys.sort
# #current_category ||= params(:category)
day_selected = params[:day_selected]
meal_selected = params[:meal_selected]
end
# GET /foods/1
# GET /foods/1.json
def show
end
# GET /foods/new
def new
#food = Food.new
end
# GET /foods/1/edit
def edit
end
# POST /foods
# POST /foods.json
def create
#food = Food.new(food_params)
respond_to do |format|
if #food.save
format.html { redirect_to foods_url, notice: 'Food was successfully created.' }
format.json { render :show, status: :created, location: #food }
else
format.html { render :new }
format.json { render json: #food.errors, status: :unprocessable_entity }
end
end
end
# PATCH/PUT /foods/1
# PATCH/PUT /foods/1.json
def update
respond_to do |format|
if #food.update(food_params)
format.html { redirect_to foods_url, notice: 'Food was successfully updated.' }
format.json { render :show, status: :ok, location: #food }
else
format.html { render :edit }
format.json { render json: #food.errors, status: :unprocessable_entity }
end
end
end
# DELETE /foods/1
# DELETE /foods/1.json
def destroy
#current_user.entries.where(food_id: "#{#food.id}").delete_all
#food.destroy
respond_to do |format|
format.html { redirect_to foods_url, notice: 'Food was successfully destroyed.' }
format.json { head :no_content }
end
end
private
# Use callbacks to share common setup or constraints between actions.
def set_food
#food = Food.find(params[:id])
end
# Never trust parameters from the scary internet, only allow the white list through.
def food_params
params.require(:food).permit(:name, :portion, :calories, :fat, :carb, :protein,
:fiber, :sugar, :category, :added_by, :cholesterol,
:potassium, :sodium, :trans_fat, :monounsaturated_fat,
:polyunsaturated_fat, :saturated_fat)
end
end
food.rb
class Food < ActiveRecord::Base
belongs_to :user
CATEGORIES = { "Dairy & Eggs" => "Dairy",
"Meat & Fish" => "Animal",
"Fruits & Vegetables" => "Plant",
"Nuts, beans & legumes" => "Nuts",
"Grains" => "Grains",
"Drinks" => "Beverages",
"Sweets & Candy" => "Sweets",
"Oils & Fats" => "Oils",
"Other" => "Other" }
validates :name, presence: true
validates :portion, presence: true
validates :calories, presence: true
validates :fat, presence: true
validates :carb, presence: true
validates :protein, presence: true
validates :category, presence: true
end
Try specifying the format in the test example, in your case json or html.
post :create, format: json, food: { ... }
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 have controllers - Survey, Questions and Options.
I cannot save Survey without creating Questions first. And in turn Questions cannot be created creating Options first. How to get "create" specs pass for Survey controller...
Surveys_Controller_spec.rb
describe "POST 'create'" do
before(:each) do
#user = FactoryGirl.create(:activated_user)
login_user(#user)
end
context "with valid parameters" do
let(:parameters) { { :title => "Title of the Survey", :description => "description", :activated => "false", :participant_info => "0", :is_draft => "true", :thank_you_message => "Thank You", :user_id => #user.id } }
it "creates a new survey" do
expect { post :create, :survey => parameters }.to change(Survey, :count).by(1)
end
Surveys_Controller.rb
def create
#survey = convert_enums(current_user.surveys.new(params[:survey]), params)
respond_to do |format|
if #survey.save
format.html { redirect_to dashboard_path, :notice => "Survey was successfully created!"}
else
format.html { render :new }
end
end
I am trying to get one of my tests to pass but I can't seem to get it:
Failures:
1) UsersController PUT 'update' failure should render the 'edit' page
Failure/Error: put :udpate, :id => #user, :user => #attr
No route matches {:id=>#<User id: 1, name: "User_Name", email: "USER#gmail.com", created_at: "2011-05-29 03:26:30", updated_at: "2011-05-29 03:26:30", encrypted_password: "fc70fcb4b094b388d87c5054ed9b0bfa06f53431d44c527e852...", salt: nil>, :user=>{:email=>"", :name=>"", :password=>"", :password_confirmation=>""}, :controller=>"users", :action=>"udpate"}
# ./spec/controllers/users_controller_spec.rb:164:in `block (4 levels) in <top (required)>'
This is the code I am trying to get it to pass with in my UsersController:
def update
#user = User.find(params[:id])
if #user.update_attributes(params[:user])
flash[:success] = "Profile updated."
redirect_to #user
else
#title = "Edit user"
render 'edit'
end
end
Any help is appreciated, thanks!
Your tests should look something like this...
describe "PUT 'update'" do
before do
#user = Factory(:user)
test_sign_in(#user)
end
describe "failure" do
before do
#attr = { :name => "", :email => "", :password => "", :password_confirmation => "" }
end
it "should render the 'edit' page" do
put :update, :id => #user, :user => #attr
response.should render_template('users/edit')
end
it "should have the right title" do
put :update, :id => #user, :user => #attr
response.should have_selector("title", :content => "Edit user")
end
end
describe "success" do
before do
#attr = { :name => "Other User", :email => "other_user#example.com", :password => "barfoo", :password_confirmation => "barfoo" }
end
it "should change the users attributes" do
put :update, :id => #user, :users => #attr
user = assigns(:user)
#user.reload
#user.name.should == user.name
#user.email.should == user.email
#user.encrypted_password == user.encrypted_password
end
it "should have a flash message" do
put :update, :id => #user, :user => #attr
flash[:success].should =~ /updated/
end
end