2012-06-12 3 views
14

Примечание: Я прочитал вопрос this и ответ, но по какой-то причине код для меня не работает. (см. ниже для ошибки, которую я получаю)Метод уничтожения RSpec (Rails Tutorial 3.2 Ch. 9, пример 10)

Упражнение 10 из главы 9 учебного пособия Rails просит: Изменить действие уничтожения [для пользователей], чтобы пользователи admin не уничтожали самих себя. (Напишите сначала тест.)

Трудная часть здесь проверяет его, потому что приложение уже скрывает ссылку «удалить» для текущего пользователя, поэтому вам нужно напрямую выполнить 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 test for destroy if no delete link, который Я копирую здесь:

От 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 Rails 3.2.3

Gems для тестирования:
группа: тест сделать камень 'RSpec рельсы', '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 ' gem 'spork', '0.9.0' gem 'factory_girl_rails', '1.4.0' конец

Подробнее Я попробовал тонну способов, чтобы попытаться имитировать нажатия на ссылку удалить, и никто не похоже на работу. Я использую камень отладчика, чтобы узнать, вызван ли метод destroy. В тесте, который нажимает на ссылку, чтобы удалить другой пользователь, метод уничтожения вызывается и она отлично работает:

it "should be able to delete another user" do 
    expect { click_link('delete') }.to change(User, :count).by(-1) 
end 

Но я ничего не пытался сформировать запрос удаления непосредственно работал, чтобы вызвать метод уничтожения.

Благодарим за помощь!

Будет

** UPDATE **

Я попытался предложение для 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 заявление, что не отпускал мою уничтожить действие дозвонились.

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/

+0

Одна вещь, которую я только что обнаружил, что не моя проблема точно, но может быть * а * проблема в том, что метод пусть ленится, так что я мог себе представить, что щуря ожидать to_not изменить функциональность. Поэтому я скорректировал код для использования let! при создании пользователя admin. –

+0

Каков сценарий, который вы тестируете? Администратор входит в систему, ссылка на удаление скрыта, но он каким-то образом обрабатывает запрос на удаление? (просто спрашиваю) –

+0

Хороший вопрос. В основном ответ заключается в том, что это было упражнение в Rails Tutorial. Оказывается, это было хорошее упражнение, потому что я изучил всевозможные вещи о куках, http-запросах, Capybara и Rack :: Test. Я предполагаю, что я могу протестировать сценарий, в котором код для скрытия ссылки на удаление не удался, и я хочу, чтобы у моего контроллера была резервная копия. –

ответ

5

Вы путаете Rspec рельсы запрос спецификацию, которые являются интеграционными тестами и выполняются в моделируемом браузере и контроллер спецификации которых тест контроллера в изоляции. delete(action, *args)get, post и т. Д.) - это метод, имитирующий запрос от ActionController :: TestCase, поэтому он недоступен в вашем тесте.

Таким образом, ваш единственный вариант - имитировать щелчок в браузере. Я не знаю, как вы прячете ссылку на удаление, если html есть, но скрытый, вы должны иметь возможность щелкнуть его. Если его нет (удаляется на стороне сервера при создании представления), вы можете использовать capibara's page.execute_script (но вы должны включить javascript для этого примера :js => true). Вы можете либо добавить обратную ссылку:

page.execute_script("$('body').append("<a href="https://stackoverflow.com/users/1" data-method="delete" rel="nofollow">Destroy</a>")") 

или сделать Ajax вызова:

page.execute_script("$.ajax({type:'DELETE',url:'/users/1'})") 

не проверял, но что-то, как это должно работать.

3

Попробуйте это:

expect { delete :destroy, :id => admin }.to_not change(User, :count) 
+0

Пробовал, такая же ошибка (см. Выше) –

+0

Это сработало для меня. Благодаря! –

6

Я решил эту же проблему, используя следующие:

describe "should not be able to delete themselves" do 
    it { expect { delete user_path(admin) }.not_to change(User, :count) } 
end 
+1

Я думаю, что это работает только в тестах контроллера, а не на интеграционных тестах. – Dean

+0

Этот тест проходит, несмотря на то, что я его вручную проверил, и он не работает. (он должен потерпеть неудачу) –

6

решение CallumD работал для меня, и, казалось, самым последовательным с методами, рекомендованными в остальной части учебника Майкла Hartl в.Но я хотел бы подтянуть синтаксис немного, чтобы сделать его более совместимым с другими спецификациями в том же учебнике:

it "should not be able to delete itself" do 
    expect { delete user_path(admin) }.not_to change(User, :count) 
end 
+1

Согласен. Я сделал то же самое. – KMcA

5

Это то, что я закончил с (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 

«уничтожен?» сам метод специфицирован Rails, поэтому ИМХО должно быть в порядке, чтобы полагаться на него.

https://github.com/rails/rails/blob/5142d5411481c893f817c1431b0869be3745060f/activerecord/lib/active_record/persistence.rb#L91

Смежные вопросы