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

"уничтожены?"сам метод определяется рельсами, поэтому ИМХО должно быть нормально полагаться на него.

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


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

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