gpt4 book ai didi

ruby-on-rails - Capybara + 远程表单请求

转载 作者:行者123 更新时间:2023-12-03 16:06:18 24 4
gpt4 key购买 nike

我有一个表格,我正在使用 Capybara 进行测试。此表单的 URL 会转到我的 Braintree 沙箱,尽管我怀疑任何远程 URL 都会出现问题。当 Capybara 单击表单的提交按钮时,请求被路由到虚拟应用程序而不是远程服务。

这是一个重现此问题的示例应用程序:https://github.com/radar/capybara_remote .运行 bundle exec ruby​​ test/form_test.rb 并且测试将通过,这不是我通常所期望的。

为什么会发生这种情况,我可以依赖的这种行为是否总是发生?

最佳答案

Mario Visic 在 Capybara 文档中指出了这个描述:

Furthermore, you cannot use the RackTest driver to test a remote application, or to access remote URLs (e.g., redirects to external sites, external APIs, or OAuth services) that your application might interact with.



但我想知道为什么,所以我开始潜水。这是我的发现:

lib/capybara/node/actions.rb
def click_button(locator)
find(:button, locator).click
end

我不在乎 find在这里,因为那行得通。这是 click这更有趣。该方法定义如下:

lib/capybara/node/element.rb
def click
wait_until { base.click }
end

不知道是什么 base是,但我看到该方法在 lib/capybara/rack_test/node.rb 中又定义了两次和 lib/capybara/selenium/node.rb .测试使用 Rack::Test而不是 Selenium,所以它可能是前者:

lib/capybara/rack_test/node.rb
def click
if tag_name == 'a'
method = self["data-method"] if driver.options[:respect_data_method]
method ||= :get
driver.follow(method, self[:href].to_s)
elsif (tag_name == 'input' and %w(submit image).include?(type)) or
((tag_name == 'button') and type.nil? or type == "submit")
Capybara::RackTest::Form.new(driver, form).submit(self)
end
end
tag_name可能不是链接——因为它是我们点击的按钮——所以它属于 elsif .绝对是 input带有 type == "submit" 的标签, 那么让我们看看 Capybara::RackTest::Form做:

lib/capybara/rack_test/form.rb
def submit(button)
driver.submit(method, native['action'].to_s, params(button))
end

好吧。 driver可能是 Rack::Test capybara 的司机。那是在做什么?

lib/capybara/rack_test/driver.rb
def submit(method, path, attributes)
browser.submit(method, path, attributes)
end

这个神秘的浏览器是什么?谢天谢地,它在同一个文件中定义:
def browser
@browser ||= Capybara::RackTest::Browser.new(self)
end

我们来看看这个类的 submit方法可以。

lib/capybara/rack_test/browser.rb
def submit(method, path, attributes)
path = request_path if not path or path.empty?
process_and_follow_redirects(method, path, attributes, {'HTTP_REFERER' => current_url})
end
process_and_follow_redirects做它在盒子上说的:
def process_and_follow_redirects(method, path, attributes = {}, env = {})
process(method, path, attributes, env)
5.times do
process(:get, last_response["Location"], {}, env) if last_response.redirect?
end
raise Capybara::InfiniteRedirectError, "redirected more than 5 times, check for infinite redirects." if last_response.redirect?
end
process 也是如此:
def process(method, path, attributes = {}, env = {})
new_uri = URI.parse(path)
method.downcase! unless method.is_a? Symbol

if new_uri.host
@current_host = "#{new_uri.scheme}://#{new_uri.host}"
@current_host << ":#{new_uri.port}" if new_uri.port != new_uri.default_port
end

if new_uri.relative?
if path.start_with?('?')
path = request_path + path
elsif not path.start_with?('/')
path = request_path.sub(%r(/[^/]*$), '/') + path
end
path = current_host + path
end

reset_cache!
send(method, path, attributes, env.merge(options[:headers] || {}))
end

是时候打开调试器看看 method在这儿。贴个 binding.pry在该方法的最后一行之前,以及 require 'pry'在测试中。原来 method:post并且,出于兴趣, new_uriURI带有我们远程表单的 URL 的对象。

这是哪里 post方法从何而来? method(:post).source_location告诉我:
["/Users/ryan/.rbenv/versions/1.9.3-p374/lib/ruby/1.9.1/forwardable.rb", 199]

这似乎不对... Capybara 有没有 def post某处?
capybara (master)★ack "def post"
lib/capybara/rack_test/driver.rb
76: def post(*args, &block); browser.post(*args, &block); end

凉爽的。我们知道 browser is a Capybara::RackTest::Browser` 对象。类(class)开头给出了下一个提示:
class Capybara::RackTest::Browser
include ::Rack::Test::Methods

我知道 Rack::Test::Methods附带 post方法。是时候潜入那颗 gem 了。

lib/rack/test.rb
def post(uri, params = {}, env = {}, &block)
env = env_for(uri, env.merge(:method => "POST", :params => params))
process_request(uri, env, &block)
end

忽略 env_for暂时是什么 process_request做?

lib/rack/test.rb
def process_request(uri, env)
uri = URI.parse(uri)
uri.host ||= @default_host

@rack_mock_session.request(uri, env)

if retry_with_digest_auth?(env)
auth_env = env.merge({
"HTTP_AUTHORIZATION" => digest_auth_header,
"rack-test.digest_auth_retry" => true
})
auth_env.delete('rack.request')
process_request(uri.path, auth_env)
else
yield last_response if block_given?

last_response
end
end

嘿, @rack_mock_session看起来很有趣。这是在哪里定义的?
rack-test (master)★ack "@rack_mock_session ="
lib/rack/test.rb
40: @rack_mock_session = mock_session
42: @rack_mock_session = MockSession.new(mock_session)

在两个地方,彼此非常接近。这些线路上和周围有什么?
def initialize(mock_session)
@headers = {}

if mock_session.is_a?(MockSession)
@rack_mock_session = mock_session
else
@rack_mock_session = MockSession.new(mock_session)
end

@default_host = @rack_mock_session.default_host
end

好的,所以它确保它是 MockSession目的。什么是 MockSession它的 request 怎么样?方法定义?
def request(uri, env)
env["HTTP_COOKIE"] ||= cookie_jar.for(uri)
@last_request = Rack::Request.new(env)
status, headers, body = @app.call(@last_request.env)
headers["Referer"] = env["HTTP_REFERER"] || ""

@last_response = MockResponse.new(status, headers, body, env["rack.errors"].flush)
body.close if body.respond_to?(:close)

cookie_jar.merge(last_response.headers["Set-Cookie"], uri)

@after_request.each { |hook| hook.call }

if @last_response.respond_to?(:finish)
@last_response.finish
else
@last_response
end
end

我将继续前进并假设 @app是机架应用程序堆栈。调用 call方法,请求直接路由到这个堆栈,而不是向外发送。

我得出的结论是,这种行为看起来像是故意的,而且我确实可以依靠这种方式。

关于ruby-on-rails - Capybara + 远程表单请求,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/16013975/

24 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com