RSpec,水豚:redirect_to 在 create/post 规范中不工作

RSpec, Capybara: redirect_to not working in create/post specs

我根据 feature_spec:

设置了一个简单的控制器

stamps_controller.rb

class StampsController < ApplicationController
  def new
    @stamp = Stamp.new
  end

  def create
    @stamp = Stamp.new(stamp_params)

    if @stamp.save
      redirect_to(stamp_url(@stamp.id), status: 201)
    else
      render 'new'
    end
  end

  def show
    @stamp = Stamp.find(params[:id])
  end

  private

  def stamp_params
    params.require(:stamp).permit(::percentage)
  end
end

specs/requests/stamps_request_spec.rb

RSpec.describe 'stamp requests', type: :request do
  describe 'stamp creation', js: true do
    before do
      FactoryBot.create_list(:domain, 2)
      FactoryBot.create_list(:label, 2)
    end

    it 'allows users to create new stamps' do
      visit new_stamp_path
      expect(page).to have_content('Percentage')

      find('#stamp_percentage').set('20')

      click_button 'Create'

      expect(current_path).to eq(stamp_path(Stamp.first.id))
    end
  end
end

根据the capybara docs

Capybara automatically follows any redirects, and submits forms associated with buttons.

但这在测试中并没有发生,而是抛出一个错误:

expected: "/stamps/1

got: "/stamps"

结果很明显:它成功创建了图章,但无法重定向到新图章。我还通过使用 binding.pry.

确认了这一点

为什么水豚不遵循文档中描述的重定向?


旁注:

  1. 如果我使用普通驱动而不是js,它甚至会失败
  2. 我研究了很多 SO 问题和文档,没有找到任何有用的东西。我无法理解的一个潜在尝试是 answer with no specifics of how to implement it.
  3. 我的配置:

support/capybara.rb

require 'capybara/rails'
require 'capybara/rspec'

Capybara.server = :puma
Capybara.register_driver :selenium do |app|
  Capybara::Selenium::Driver.new(app, browser: :firefox, marionette: true)
end

Capybara.javascript_driver = :selenium

RSpec.configure do |config|
  config.include Capybara::DSL
end

spec_helper.rb

ENV['RAILS_ENV'] ||= 'test'
require File.expand_path('../config/environment', __dir__)
require 'rspec/rails'
require 'factory_bot_rails'
require 'pundit/matchers'

Dir[Rails.root.join('spec/support/**/*.rb')].each { |f| require f }

RSpec.configure do |config|
  # .
  # unrelated stuff
  # .
end

问题可能是 current_path 在提交表单后需要一些时间才能更改。如果您将 sleep(x) 放在 expect(current_path) 之前,您的代码将起作用。

相反,您应该使用具有所谓 "waiting behaviour" 的方法,例如 has_current_path?have_current_pathassert_current_path:

expect(page).to have_current_path(stamp_path(Stamp.first.id))

expect(page.has_current_path?(stamp_path(Stamp.first.id))).to eq true

您的测试中有很多问题。

首先,水豚不适用于 request 规范 - https://relishapp.com/rspec/rspec-rails/docs/request-specs/request-spec - but should instead be used with feature/system tests. Once you've fixed that you should no longer need to include Capybara into every RSpec test and should remove the config.include Capybara::DSL from you config (when you require capybara/rspec it includes Capybara into the test types it should be included in - https://github.com/teamcapybara/capybara/blob/master/lib/capybara/rspec.rb#L10)

其次,click_button不能保证等待它触发的任何操作完成。因此,在尝试访问将由该操作创建的任何数据库对象之前,您需要等待可视页面更改(从技术上讲,您实际上根本不应该在功能规范中进行直接数据库访问,但如果您要...)

  click_button 'Create' 
  expect(page).to have_text('Stamp created!') # Whatever message is shown after creation
  # Now you can safely access the DB for the created stamp

第三,正如@chumakoff 所指出的,您不应该对 Capybara 使用静态匹配器,而应该使用 Capybara 提供的匹配器

  click_button 'Create' 
  expect(page).to have_text('Stamp created!') # Whatever message is shown after creation
  expect(page).to have_path(stamp_path(Stamp.first.id))

最后,您应该查看您的 test.log 并使用 save_and_open_screenshot 来查看您的控制器实际做了什么 - 实际上在创建时出现错误导致您的应用程序重定向到/stamps 并显示错误消息(这也意味着您的测试数据库实际上并未在测试之间重置,或者您显示的工厂正在创建嵌套记录等)。

更新: 重新阅读您的控制器代码后,我注意到您将 201 状态代码传递给 redirect_to201 实际上不会进行重定向 - 来自 redirect_to 文档 - https://api.rubyonrails.org/classes/ActionController/Redirecting.html#method-i-redirect_to

Note that the status code must be a 3xx HTTP code, or redirection will not occur.

对于其他来这里的人,另一种可能的解决方案是增加等待时间。全局或每次点击

# Globally
Capybara.default_max_wait_time = 5

# Per Click
find("#my-button").click(wait: 5)