Аутентифицированный маршрут не работает для теста Rspec

Я следую этому сообщению о настройке аутентификации в маршрутах моего приложения Rails 4.

Вот мой файл route.rb:

Rails.application.routes.draw do

  devise_for :employees, :controllers => { registrations: 'employees/registrations' }
  devise_for :clients


  authenticate :employee do
    resources :quotation_requests, only: [:show, :edit,:index, :update, :destroy]
  end

  resources :quotation_requests, only: [:new, :create]

  get '/dashboard' => 'dashboard#show', as: 'show_dashboard' 
  root to: 'home#index'
end

Вот мой файл quotation_requests_controller_spec.rb:

require 'rails_helper'

RSpec.describe QuotationRequestsController, type: :controller do


    describe "GET index" do
        it "renders :index template" do
            get :index
            expect(response).to render_template(:index)
        end

        it "assigns quotation requests to template" do
            quotation_requests = FactoryGirl.create_list(:quotation_request, 3)
            get :index
            expect(assigns(:quotation_requests)).to match_array(quotation_requests)
        end

    end

    describe "GET edit" do
        let(:quotation_request) { FactoryGirl.create(:quotation_request)}

        it "renders :edit template" do
            get :edit, id: quotation_request
            expect(response).to render_template(:edit)
        end
        it "assigns the requested quotation request to template" do
            get :edit, id: quotation_request
            expect(assigns(:quotation_request)).to eq(quotation_request)
        end
    end

    describe "PUT update" do
        let(:quotation_request) { FactoryGirl.create(:quotation_request)}

        context "valid data" do
            new_text = Faker::Lorem.sentence(word_count=500)
            let(:valid_data) { FactoryGirl.attributes_for(:quotation_request, sample_text: new_text)}

            it "redirects to quotation_request#showtemplate" do
                put :update, id: quotation_request, quotation_request: valid_data
                expect(response).to redirect_to(quotation_request)
            end
            it "updates quotation request in the database" do
                put :update, id: quotation_request, quotation_request: valid_data
                quotation_request.reload #need to reload the object because we have just updated it in the database so need to get the new values
                expect(quotation_request.sample_text).to eq(new_text)
            end
        end

        context "invalid data" do
            let(:invalid_data) { FactoryGirl.attributes_for(:quotation_request, sample_text: "", number_of_words: 400)}

            it "renders the :edit template" do
                put :update, id: quotation_request, quotation_request: invalid_data
                expect(response).to render_template(:edit)
            end
            it "does not update the quotation_request in the database" do
                put :update, id: quotation_request, quotation_request: invalid_data
                quotation_request.reload
                expect(quotation_request.number_of_words).not_to eq(400)
            end
        end
    end

    describe "GET new", new: true do
        it "renders :new template" do
            get :new
            expect(response).to render_template(:new)
        end
        it "assigns new QuotationRequest to @quotation_request" do
            get :new
            expect(assigns(:quotation_request)).to be_a_new(QuotationRequest)
        end
    end

    describe "GET show" do

        #this test requires that there be a quotation request in the database
        let(:quotation_request) { FactoryGirl.create(:quotation_request) }

        context 'invalid request' do

            it "does not render :show template if an employee or client is not signed in" do

                #setup
                quotation_request =  create(:quotation_request)

                #exercise
                get :show, id: quotation_request

                #verification
                expect(response).to_not render_template(:show)

            end

        end

        context 'valid request' do

            sign_in_proofreader

            it "renders :show template if an employee or client is signed in" do

                #setup
                quotation_request =  create(:quotation_request)


                #exercise
                get :show, id: quotation_request

                #verification
                expect(response).to render_template(:show)
            end

            it "assigns requested quotation_request to @quotation_request" do
                get :show, id:  quotation_request
                expect(assigns(:quotation_request)).to eq(quotation_request)
            end

        end
    end

    describe "POST create", post: true do
        context "valid data" do 
            let(:valid_data) {FactoryGirl.nested_attributes_for(:quotation_request)}

            it "redirects to quotation_requests#show" do
                post :create, quotation_request: valid_data
                expect(response).to redirect_to(quotation_request_path(assigns[:quotation_request]))
            end

            it "creates new quotation_request in database" do
            expect {
                post :create, quotation_request: valid_data
                }.to change(QuotationRequest, :count).by(1)
            end
        end

        context "invalid data" do
        let(:invalid_data) {FactoryGirl.nested_attributes_for(:quotation_request).merge(sample_text: 'not enough sample text')}

            it "renders :new template" do
                post :create, quotation_request: invalid_data
                expect(response).to render_template(:new)
            end

            it "doesn't creates new quotation_request in database" do
                expect {
                    post :create, quotation_request: invalid_data
                }.not_to change(QuotationRequest, :count)
            end
        end
    end

    describe "DELETE destroy" do

        let(:quotation_request) { FactoryGirl.create(:quotation_request) }

        it "redirects to the quotation request#index" do
            delete :destroy, id: quotation_request
            expect(response).to redirect_to(quotation_requests_path)
        end
        it "delets the quotation request from the database" do
            delete :destroy, id: quotation_request
            expect(QuotationRequest.exists?(quotation_request.id)).to be_falsy
        end

    end
end

Мой quotation_requests_controller.rb

class QuotationRequestsController < ApplicationController
# before_action :authenticate_employee!, :only => [:show]

    def index
        @quotation_requests =  QuotationRequest.all
    end

    def new
        @quotation_request = QuotationRequest.new
        @quotation_request.build_client
    end

    def edit
        @quotation_request = QuotationRequest.find(params[:id])
    end

    def create
      client = Client.find_or_create(quotation_request_params[:client_attributes])
      @quotation_request = QuotationRequest.new(quotation_request_params.except(:client_attributes).merge(client: client))
      if @quotation_request.save
        ClientMailer.quotation_request_created(client.email, @quotation_request.id).deliver_now
        redirect_to @quotation_request, notice: 'Thank you.'
      else
        render :new
      end
    end

    def show
        @quotation_request = QuotationRequest.find(params[:id])
    end

    def update
        @quotation_request = QuotationRequest.find(params[:id])
        if @quotation_request.update(quotation_request_params)
            redirect_to @quotation_request
        else
            render :edit
        end
    end

    def destroy
        QuotationRequest.destroy(params[:id])
        redirect_to quotation_requests_path
    end

    private

    def quotation_request_params
        params.require(:quotation_request).permit(:number_of_words, :return_date, :sample_text, :client_attributes => [:first_name, :last_name, :email])
    end

end

Я знаю, что аутентификация маршрутов работает, потому что, если я тестирую их в браузере, я перенаправляюсь на страницу sign_in. Однако тесты не проходят в Rspec.

если я поместил этот код в quotation_requests_controller.rb:

 before_action :authenticate_employee!, :only => [:show]

Проходят тесты rspec. Поэтому по какой-то причине Rspec не регистрирует аутентификацию маршрутов.

Вот результат Rspec для тестов, запущенных с проверенными маршрутами:

QuotationRequestsController
  GET index
    valid request
      renders :index template for signed in employee
      assigns quotation requests to template
    invalid request
      does not render :index template without a signed in employee (FAILED - 1)
  GET edit
    valid request
      renders :edit template with a signed in employee
      assigns the requested quotation request to template
    invalid request
      does not render the :edit template without a signed in employee (FAILED - 2)
  PUT update
    valid request
      valid data
        redirects to quotation_request#showtemplate
        updates quotation request in the database
      invalid data
        renders the :edit template
        does not update the quotation_request in the database
    invalid request
      redirects user to the sign in page (FAILED - 3)
  GET new
    renders :new template
    assigns new QuotationRequest to @quotation_request
  GET show
    invalid request
      does not render :show template if an employee or client is not signed in (FAILED - 4)
    valid request
      renders :show template if an employee or client is signed in
      assigns requested quotation_request to @quotation_request
  POST create
    valid data
      redirects to quotation_requests#show
      creates new quotation_request in database
    invalid data
      renders :new template
      doesn't creates new quotation_request in database
  DELETE destroy
    valid request
      redirects to the quotation request#index
      delets the quotation request from the database
    invalid request
      does not delete the quotation request without a signed in employee (FAILED - 5)

Failures:

  1) QuotationRequestsController GET index invalid request does not render :index template without a signed in employee
     Failure/Error: expect(response).to_not render_template(:index)
       Didn't expect to render index
     # ./spec/controllers/quotation_requests_controller_spec.rb:43:in `block (4 levels) in <top (required)>'
     # -e:1:in `<main>'

  2) QuotationRequestsController GET edit invalid request does not render the :edit template without a signed in employee
     Failure/Error: expect(response).to_not render_template(:edit)
       Didn't expect to render edit
     # ./spec/controllers/quotation_requests_controller_spec.rb:92:in `block (4 levels) in <top (required)>'
     # -e:1:in `<main>'

  3) QuotationRequestsController PUT update invalid request redirects user to the sign in page
     Failure/Error: expect(response).to_not redirect_to(quotation_request)
       Didn't expect to redirect to #<QuotationRequest:0x007fe7eb69c8c0>
     # ./spec/controllers/quotation_requests_controller_spec.rb:182:in `block (4 levels) in <top (required)>'
     # -e:1:in `<main>'

  4) QuotationRequestsController GET show invalid request does not render :show template if an employee or client is not signed in
     Failure/Error: expect(response).to_not render_template(:show)
       Didn't expect to render show
     # ./spec/controllers/quotation_requests_controller_spec.rb:217:in `block (4 levels) in <top (required)>'
     # -e:1:in `<main>'

  5) QuotationRequestsController DELETE destroy invalid request does not delete the quotation request without a signed in employee
     Failure/Error: expect(QuotationRequest.exists?(quotation_request.id)).to be_truthy

       expected: truthy value
            got: false
     # ./spec/controllers/quotation_requests_controller_spec.rb:361:in `block (4 levels) in <top (required)>'
     # -e:1:in `<main>'

Finished in 2.11 seconds (files took 1.75 seconds to load)
23 examples, 5 failures

Failed examples:

rspec ./spec/controllers/quotation_requests_controller_spec.rb:37 # QuotationRequestsController GET index invalid request does not render :index template without a signed in employee
rspec ./spec/controllers/quotation_requests_controller_spec.rb:83 # QuotationRequestsController GET edit invalid request does not render the :edit template without a signed in employee
rspec ./spec/controllers/quotation_requests_controller_spec.rb:171 # QuotationRequestsController PUT update invalid request redirects user to the sign in page
rspec ./spec/controllers/quotation_requests_controller_spec.rb:208 # QuotationRequestsController GET show invalid request does not render :show template if an employee or client is not signed in
rspec ./spec/cont

Почему написанные мной маршруты не работают в тестах Rspec?

Ответ 1

Полагаю, вы используете rspec-rails в своем приложении rails. Rspec-rails настраивает для вас множество удобных методов, но также вводит черную магию, что может привести к неожиданным результатам - например, это.

Как вы можете видеть здесь, это объясняется в комментариях для спецификаций контроллера:

# Supports a simple DSL for specifying behavior of ApplicationController.
# Creates an anonymous subclass of ApplicationController and evals the
# `body` in that context. Also sets up implicit routes for this
# controller, that are separate from those defined in "config/routes.rb".

Я предполагаю, что логика здесь заключается в том, что функции контроллера отличаются от маршрутизации и должны тестироваться отдельно (и, действительно, rspec-rails предлагает тестовую группу для маршрутизации), поэтому нам не нужны маршруты для спецификаций контроллера, то есть вы должны быть возможность тестирования вашего контроллера без настройки маршрутов.

В моем оппоненте тестирование перенаправления для не прошедших проверку подлинности пользователей является скорее интеграционным тестом, так как требует совместной работы нескольких частей вашего приложения и как такового не следует тестировать в контексте контроллера, а скорее как функцию в некоторых Blackbox.

Вы можете написать тесты интеграции, поместив их в один из этих каталогов spec/requests, spec/api, and spec/integration или явно объявив их тип с помощью

RSpec.describe "Something", type: :request do

или поместите его в spec/features или объявите тип как

RSpec.describe "Something", type: :feature do

в зависимости от того, на каком уровне вы хотите протестировать перенаправление (что означает: проверять только цикл запроса-ответа или запускать его в моделированном браузере). Дополнительную информацию см. В документации для тестов интеграции на странице rspec-rails github.