Примечание. Я прочитал этот вопрос и ответ, но по какой-то причине код не работает для меня. (см. ниже для ошибки, которую я получаю)
Упражнение 10 из Главы 9 Учебного курса Rails предлагает вам: Изменить действие уничтожения [для пользователей], чтобы пользователи администраторов не уничтожали себя. (Сначала напишите тест.)
Трудная часть здесь - это тестирование, потому что приложение уже скрывает ссылку "удалить" для текущего пользователя, поэтому вам нужно напрямую выполнить HTTP-запрос.
Я получил код и протестировал его, удалив фрагмент кода, который скрывает ссылку удаления для текущего пользователя. Разумеется, если я нажму ссылку на удаление для текущего зарегистрированного пользователя, он перенаправляет меня и дает мне уведомление.
От users_controller.rb
def destroy
@user = User.find(params[:id])
if current_user?(@user)
redirect_to users_path, notice: "You can't destroy yourself."
else
@user.destroy
flash[:success] = "User destroyed."
redirect_to users_path
end
end
Проблема, с которой я столкнулась, заключается в написании тестов для этого, которые отправят запрос на удаление и вызовет метод destroy. Я попробовал решение из Rspec test для уничтожить, если не удалить ссылку, которую я копирую здесь:
Из user_pages_spec.rb
describe "destroy" do
let(:admin) { FactoryGirl.create(:admin) }
it "should not allow the admin to delete herself" do
sign_in admin
#expect { delete user_path(admin), method: :delete }.should change(User, :count)
expect { delete :destroy, :id => admin.id }.should_not change(User, :count)
end
end
Но когда я запускаю это, я получаю эту ошибку от RSpec
Failures:
1) User Pages destroy should not allow the admin to delete herself
Failure/Error: expect { delete :destroy, :id => admin.id }.should_not change(User, :count)
ArgumentError:
bad argument (expected URI object or URI string)
# ./spec/requests/user_pages_spec.rb:180:in `block (4 levels) in <top (required)>'
# ./spec/requests/user_pages_spec.rb:180:in `block (3 levels) in <top (required)>'
Итак, мои вопросы: 1) Почему этот код выше сбой? 2) Как смоделировать "удаление", чтобы вызвать действие destroy в моем контроллере?
Окружающая среда: Mac OS X ruby 1.9.3p194 Rails 3.2.3
Драгоценные камни для тестирования:
группа: test do gem 'rspec-rails', '2.9.0' gem 'capybara', '1.1.2' gem 'rb-fsevent', '0.4.3.1',: require = > false gem 'growl', '1.0.3' gem 'guard-spork', '0.3.2' gem 'spork', '0.9.0' gem 'factory_girl_rails', '1.4.0'
конец
Дополнительная информация Я попытался использовать тонну, чтобы попытаться имитировать щелчок по ссылке удаления, и ни один из них не работает. Я использую камень отладчика, чтобы узнать, вызван ли метод destroy. В тесте, который нажимает на ссылку для удаления другого пользователя, вызывается метод destroy, и он отлично работает:
it "should be able to delete another user" do
expect { click_link('delete') }.to change(User, :count).by(-1)
end
Но ничто из того, что я пытался сгенерировать запрос удаления напрямую, не работал, чтобы вызвать метод destroy.
Спасибо за вашу помощь!
Воля
** ОБНОВЛЕНИЕ **
Я попробовал предложение DVG:
describe "destroy" do
let(:admin) { FactoryGirl.create(:admin) }
it "should not allow the admin to delete herself" do
sign_in admin
#expect { delete user_path(admin), method: :delete }.should change(User, :count)
expect { delete :destroy, :id => admin }.to_not change(User, :count)
end
end
И получил эту ошибку:
6) User Pages destroy should not allow the admin to delete herself
Failure/Error: expect { delete :destroy, :id => admin }.to_not change(User, :count)
ArgumentError:
bad argument (expected URI object or URI string)
# ./spec/requests/user_pages_spec.rb:190:in `block (4 levels) in <top (required)>'
# ./spec/requests/user_pages_spec.rb:190:in `block (3 levels) in <top (required)>'
Решение
Я понял это после НАВСЕГДА.
Мне пришлось использовать Rack:: Test для запроса DELETE, но Capybara и Rack:: Test не используют одно и то же MockSession, поэтому мне пришлось вытащить файлы cookie: remember_token и:! sample_app_session и поместить их в запрос DELETE вручную. Вот что сработало. (другая проблема, которую я имел, перечисленная ниже, заключалась в том, что у меня было выражение force_ssl, которое не позволяло вызвать действие destroy.
describe "destroy" do
let!(:admin) { FactoryGirl.create(:admin) }
before do
sign_in admin
end
it "should delete a normal user" do
user = FactoryGirl.create(:user)
expect { delete user_path(user), {},
'HTTP_COOKIE' => "remember_token=#{admin.remember_token},
#{Capybara.current_session.driver.response.headers["Set-Cookie"]}" }.
to change(User, :count).by(-1)
end
it "should not allow the admin to delete herself" do
expect { delete user_path(admin), {},
'HTTP_COOKIE' => "remember_token=#{admin.remember_token},
#{Capybara.current_session.driver.response.headers["Set-Cookie"]}" }.
to_not change(User, :count)
end
end
У меня был оператор force_ssl после моего before_filters в моем users_controller.rb, и это как-то отбросило вещи, так что я никогда не попадал в действие destroy.
class UsersController < ApplicationController
before_filter :signed_in_user, only: [:edit, :update, :index]
before_filter :existing_user, only: [:new, :create]
before_filter :correct_user, only: [:edit, :update]
before_filter :admin_user, only: :destroy
#force_ssl
def index
@users = User.paginate(page: params[:page])
end
def show
@user = User.find(params[:id])
@microposts = @user.microposts.paginate(page: params[:page])
end
def destroy
@user = User.find(params[:id])
if current_user?(@user)
redirect_to users_path, notice: "You can't destroy yourself."
else
@user.destroy
flash[:success] = "User destroyed."
redirect_to users_path
end
end
Это было полезно при доступе к решению
https://gist.github.com/484787
http://collectiveidea.com/blog/archives/2012/01/05/capybara-cucumber-and-how-the-cookie-crumbles/