Здравствуйте, записался на курс. При этом ставил галочку на "обучаться с тьютором". На email пришло письмо, о том, что записался на самостоятельное изучение курса. Как выбрать тьютора? |
Слежение за сообщениями пользователей
Стандартный способ реализации кнопки "читать" (follow)
Теперь, когда наши представления в порядке, пришло время получить рабочие follow/unfollow кнопки. Тесты для этих кнопок комбинируют множество техник тестирования о которых было рассказано в этом учебнике и представляют из себя хорошее упражнение в чтении кода. Изучайте Листинг 11.32 до тех пор пока не убедитесь что вы понимаете что и почему мы тестируем. (Листинг 11.32 содержит одно небольшое упущение в безопасности; посмотрим, сможете ли вы выявить его. Мы скоро расскажем о нем.) Особенно обратите внимание на использование метода have_xpath использующего продвинутую и мощную технику XPath для навигации по XML документам (включая HTML5). Вы можете узнать больше о XPath с помощью поискового запроса XPath syntax.
require 'spec_helper' describe "User pages" do . . . describe "profile page" do let(:user) { FactoryGirl.create(:user) } . . . describe "follow/unfollow buttons" do let(:other_user) { FactoryGirl.create(:user) } before { sign_in user } describe "following a user" do before { visit user_path(other_user) } it "should increment the followed user count" do expect do click_button "Follow" end.to change(user.followed_users, :count).by(1) end it "should increment the other user's followers count" do expect do click_button "Follow" end.to change(other_user.followers, :count).by(1) end describe "toggling the button" do before { click_button "Follow" } it { should have_xpath("//input[@value='Unfollow']") } end end describe "unfollowing a user" do before do user.follow!(other_user) visit user_path(other_user) end it "should decrement the followed user count" do expect do click_button "Unfollow" end.to change(user.followed_users, :count).by(-1) end it "should decrement the other user's followers count" do expect do click_button "Unfollow" end.to change(other_user.followers, :count).by(-1) end describe "toggling the button" do before { click_button "Unfollow" } it { should have_xpath("//input[@value='Follow']") } end end end end . . . endЛистинг 11.32. Тесты для Follow/Unfollow кнопки. spec/requests/user_pages_spec.rb
Листинг 11.32 тестирует кнопки кликая по ним и проверяя соответствующее поведение. Написание реализации подразумевает чуть более глубокое погружения в тему: following и unfollowing включает создание и уничтожение взаимоотношений, что означает необходимость определения create и destroy действий в контроллере Relationships (который нам еще нужно создать). Хотя кнопки появляются только для вошедших пользователей, что дает нам безопасность верхнего уровня, тесты в Листинге 11.32 упускают из виду безопасность на уровне контроллера, а именно: сами create и destroy должны быть доступны только для вошедших пользователей. (Это та самая уязвимость о которой мы говорили выше.) Листинг 11.33 выражает эти требования с помощью post и delete методов вызывающих эти действия напрямую.
require 'spec_helper' describe "Authentication" do . . . describe "authorization" do describe "for non-signed-in users" do let(:user) { FactoryGirl.create(:user) } . . . describe "in the Relationships controller" do describe "submitting to the create action" do before { post relationships_path } specify { expect(response).to redirect_to(signin_path) } end describe "submitting to the destroy action" do before { delete relationship_path(1) } specify { expect(response).to redirect_to(signin_path) } end end . . . end end endЛистинг 11.33. Тестирование авторизации контроллера Relationships. spec/requests/authentication_pages_spec.rb
Обратите внимание, что, для того чтобы избежать лишней работы по созданию практически бесполезного объекта Relationship, тест delete хардкодит id 1 в именованном маршруте:
before { delete relationship_path(1) }
Это работает из-за того что пользователь должен быть перенаправлен прежде чем приложение даже попытается обратиться к взаимоотношению с этим id.
Код контроллера, необходимый для прохождения этих тестов удивительно краток: мы просто вытягиваем читаемого или читающего пользователя, а затем читаем или не читаем его сообщения используя соответствующий служебный метод. Полная реализация представлена в Листинге 11.34.
class RelationshipsController < ApplicationController before_action :signed_in_user def create @user = User.find(params[:relationship][:followed_id]) current_user.follow!(@user) redirect_to @user end def destroy @user = Relationship.find(params[:id]).followed current_user.unfollow!(@user) redirect_to @user end endЛистинг 11.34. Контроллер Relationships. app/controllers/relationships_controller.rb
В Листинге 11.34 мы можем видеть почему уязвимость отмеченная выше является незначительной: если невошедший пользователь попробовал бы обратиться к любому из действий напрямую (например, с помощью инструмента командной строки вроде curl), current_user был бы nil и в обоих случаях вторая строка действий вызвала бы исключение, что привело бы к ошибке, но не нанесло бы вреда приложению или его данным. Однако лучше на это не полагаться, так что мы предприняли дополнительный шаг и добавили дополнительный уровень безопасности.
С этим ядро функциональности follow/unfollow завершено, и любой пользователь может читать (или не читать) сообщения любого другого пользователя, что вам стоит проверить и в вашем браузере и запустив набор тестов:
$ bundle exec rspec spec/