RSpec test destroy method (Rails Tutorial 3.2 Ch. 9, Ex. Десять)
примечание: Я читал этой вопрос и ответ, но по какой-то причине код не работает для меня. (см. ниже для ошибки, которую я получаю)
упражнение 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
проблема, с которой я сталкиваюсь, заключается в написании тестов для этого, которые отправят запрос на удаление и вызовут уничтожить метод. Я попробовал решение из RSpec тест для уничтожения, если нет удалить ссылку, который я копирую сюда:
от 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 OSX Руби 1.9.3p194 Рельсы 3.2.3
драгоценные камни для тестирования:
группа :тест делать
gem 'rspec-rails', '2.9.0'
драгоценный камень "капибара", "1.1.2"
gem 'rb-fsevent', '0.4.3.1',: require => false
gem 'growl', '1.0.3'
gem 'guard-spork', '0.3.2'
ловилка камень'', '0.9.0'
gem 'factory_girl_rails', '1.4.0'
конец
Подробнее Я пробовал Т способов попытаться имитировать нажатие на ссылку удаления, и ни один из них не работает. Я использую отладчик gem для посмотрите, вызывается ли метод 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 для выдачи запроса на удаление, но Capybara и Rack:: Test не разделяют одну и ту же MockSession, поэтому мне пришлось вытащить :remember_token и:!sample_app_session cookies и поместите их в запрос на удаление вручную. Вот что получилось. (другая проблема, которую я имел, перечисленная ниже, заключалась в том, что у меня был оператор force_ssl, который не позволял уничтожить действие получить называется.
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 и это как-то выбрасывало вещи, поэтому я никогда не добирался до действия уничтожения.
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/
5 ответов
вы путаете спецификации запроса rspec-rails, которые являются интеграционными тестами и выполняются в имитируемых спецификациях браузера и контроллера, которые тестируют контроллер изолированно. delete(action, *args)
(и get
, post
и так далее) - это метод, имитирующий запрос от ActionController::TestCase, поэтому он недоступен в вашем тесте.
таким образом, ваш единственный вариант-имитировать щелчок в браузере. Я не знаю, как скрыть ссылку на удаление, Если html есть, но скрыт, вы должны быть в состоянии щелкнуть его. Если ее там нет (снято на стороне сервера при генерации вид) можно использовать капибара это page.execute_script
(но вы должны включить javascript для этого примера :js => true
). Вы можете либо добавить ссылку:
page.execute_script("$('body').append("<a href="/users/1" data-method="delete" rel="nofollow">Destroy</a>")")
или сделать AJAX-вызов:
page.execute_script("$.ajax({type:'DELETE',url:'/users/1'})")
не тестировал, но что-то вроде этого должно работать.
Я решил эту же проблему, используя следующие:
describe "should not be able to delete themselves" do
it { expect { delete user_path(admin) }.not_to change(User, :count) }
end
решение Калламда сработало для меня и казалось наиболее совместимым с методами, рекомендованными в остальной части учебника Майкла Хартла. Но я хотел немного подтянуть синтаксис, чтобы сделать его более совместимым с другими спецификациями в том же учебнике:
it "should not be able to delete itself" do
expect { delete user_path(admin) }.not_to change(User, :count)
end
Это то, что я закончил (Rspec 3.2):
describe 'DELETE destroy' do
before :each do
delete :destroy, { id: current_partner_role }
end
it 'destroys role' do
expect(assigns(:role).destroyed?).to be true
end
"уничтожены?"сам метод определяется рельсами, поэтому ИМХО должно быть нормально полагаться на него.