在 Capybara 中请求 GET,而它应该是 POST

Request in Capybara a GET, when it should be a POST

所以这是一个非常基本的 Rails 5 应用程序,使用 Docker,其中包括一些我从头开始构建的用户身份验证(不使用 Devise 等)。现在,我想开始学习 Capybara 的请求规范,但我遇到了一个看起来很奇怪的问题。

这是我的登录表单 (sessions.new.erb):

<%= form_tag sessions_path do %>
          <form class="m-t" role="form" action="/">
              <div class="form-group">
                <%= text_field_tag :email, params[:email], class: 'form-control', placeholder: "Email Address", required: "" %>
              </div>
              <div class="form-group">
                <%= password_field_tag(:password, nil, class: 'form-control', placeholder: "Password", required: "") %>
              </div>
              <div class="form-group">
                        <%= check_box_tag :remember_me, 1, params[:remember_me] %>
                        <%= label_tag :remember_me %>
                      </div>
              <div class="actions"><%= submit_tag "Log In", class: "btn btn-primary block full-width m-b" %></div>
          </form>
        <% end %>

还有我的requests/sessions_spec.rb

require "rails_helper"

RSpec.feature "Login", :type => :feature do
  scenario "handles wrong email and password gracefully" do
    visit login_path
    fill_in "Email Address", :with => "something"
    fill_in "Password", :with => "something"
    click_button "Log In"

    expect(page).to have_text("Email or password incorrect")
  end
end

现在,如果您手动测试它,它就可以工作,所以我认为 Capybara 会看到同样的东西。但它一直失败。我已经配置了应用程序,这样如果您尝试访问受保护的控制器但您没有登录,它会将您重定向到 /login 并闪烁一条消息说 Please log in to see this page。 Rspec 测试返回了那个,这很奇怪 - 这表明 Capybara 正在尝试访问另一个页面。

所以我跟踪了测试日志 (docker-compose run web tail -f log/test.log)

我的发现让我感到困惑:

Started GET "/login" for 127.0.0.1 at 2017-10-17 06:59:26 +0000
Processing by SessionsController#new as HTML
  Rendering sessions/new.html.erb within layouts/empty
  Rendered sessions/new.html.erb within layouts/empty (1.1ms)
Completed 200 OK in 6ms (Views: 6.1ms | ActiveRecord: 0.0ms)
Started GET "/?email=something&password=[FILTERED]&commit=Log+In" for 127.0.0.1 at 2017-10-17 06:59:26 +0000
Started GET "/locations" for 127.0.0.1 at 2017-10-17 06:59:26 +0000
Processing by LocationsController#index as HTML
Redirected to http://www.example.com/login
Filter chain halted as :authenticate rendered or redirected
Completed 302 Found in 2ms (ActiveRecord: 0.0ms)
Started GET "/login" for 127.0.0.1 at 2017-10-17 06:59:26 +0000
Processing by SessionsController#new as HTML
  Rendering sessions/new.html.erb within layouts/empty
  Rendered sessions/new.html.erb within layouts/empty (1.1ms)
Completed 200 OK in 6ms (Views: 4.9ms | ActiveRecord: 0.0ms)
   (0.4ms)  ROLLBACK

第一位没问题,GET 登录由 SessionsController#new 处理。但是,(见第 6 行)出于某种原因,Capybara 试图获取根 URL,将 email/password 参数传入。我的根 URL 映射到 LocationsController#index,不允许用户访问,因此被重定向回 /login 并显示消息 Please log in to see this page。该按钮的实际作用是向 SessionsController#create 发送 POST。如果您在手动执行时查看日志,就会发生这种情况:

web_1  | Started POST "/sessions" for 172.18.0.1 at 2017-10-17 07:02:19+0000
web_1  | Processing by SessionsController#create as HTML

我不明白为什么在 Capybara 中,当您按下按钮时,它执行的请求与您手动单击按钮时执行的请求完全不同。

不胜感激!

首先澄清几点。

  1. 您不是在编写请求规范,而是在编写功能规范(使用 RSpec.feature:type => :feature
  2. 可以证明
  3. 使用 RSpec.feature 时不需要指定 :type => :feature,因为它已经设置好了。

现在开始您的问题。因为 form_tag 创建了一个 <form> 元素,所以你在你的视图代码中嵌套了表单,然后你在其中直接有另一个 <form> 元素(注意:post 实际的总是更好HTML 而不是 erb,这样人们就可以看到实际的 HTML 是什么)。结合您似乎正在使用 rack-test 驱动程序(没有 js: true 元数据)的事实,当 HTML 无效(嵌套形式是),你最终会得到你当前的行为。我猜想当你在真正的浏览器中使用它时,内部表单元素会被忽略,而外部表单元素的方法属性等于 "post",因此它会被 posted。当使用 rack-test 时,它可能会提交没有 method 属性的内部表单元素,因此默认为 "get"。从您的视图中删除无关的 <form class="m-t" role="form" action="/"> 表单元素,事情应该会起作用。