问题
Just switched from Cucumber+Webrat to Cucumber+Capybara and I am wondering how you can POST content to a URL in Capybara.
In Cucumber+Webrat I was able to have a step:
When /^I send \"([^\\\"]*)\" to \"([^\\\"]*)\"$/ do |file, project|
proj = Project.find(:first, :conditions => \"name=\'#{project}\'\")
f = File.new(File.join(::Rails.root.to_s, file))
visit \"project/\" + proj.id.to_s + \"/upload\",
:post, {:upload_path => File.join(::Rails.root.to_s, file)}
end
However, the Capybara documentation mentions:
The visit method only takes a single parameter, the request method is always GET.always GET.
How do I modify my step so that Cucumber+Capybara does a POST to the URL?
回答1:
More recently I found this great blog post. Which is great for the cases like Tony and where you really want to post something in your cuke:
For my case this became:
def send_log(file, project)
proj = Project.find(:first, :conditions => "name='#{project}'")
f = File.new(File.join(::Rails.root.to_s, file))
page.driver.post("projects/" + proj.id.to_s + "/log?upload_path=" + f.to_path)
page.driver.status_code.should eql 200
end
回答2:
You could do this:
rack_test_session_wrapper = Capybara.current_session.driver
rack_test_session_wrapper.submit :post, your_path, nil
- You can replace
:post
which whatever method you care about e.g.:put
or:delete
. - Replace
your_path
with the Rails path you want e.g.rack_test_session_wrapper.submit :delete, document_path(Document.last), nil
would delete the last Document in my app.
回答3:
If your driver doesn't have post
(Poltergeist doesn't, for example), you can do this:
session = ActionDispatch::Integration::Session.new(Rails.application)
response = session.post("/mypath", my_params: "go_here")
But note that this request happens in a new session, so you will have to go through the response
object to assert on it.
As has been stated elsewhere, in a Capybara test you typically want to do POSTs by submitting a form just like the user would. I used the above to test what happens to the user if a POST happens in another session (via WebSockets), so a form wouldn't cut it.
Docs:
- http://api.rubyonrails.org/classes/ActionDispatch/Integration/Session.html
- http://api.rubyonrails.org/classes/ActionDispatch/Integration/RequestHelpers.html
回答4:
Capybara's visit
only does GET requests. This is by design.
For a user to perform a POST
, he must click a button or submit a form. There is no other way of doing this with a browser.
The correct way to test this behaviour would be:
visit "project/:id/edit" # This will only GET
attach_file "photo", File.open('cute_photo.jpg')
click_button 'Upload' # This will POST
If you want to test an API, I recommend using spec/request
instead of cucumber, but that's just me.
回答5:
I know the answer has already been accepted, but I'd like to provide an updated answer. Here is a technique from Anthony Eden and Corey Haines which passes Rack::Test to Cucumber's World object:
Testing REST APIs with Cucumber and Rack::Test
With this technique, I was able to directly send post requests within step definitions. While writing the step definitions, it was extremely helpful to learn the Rack::Test API from it's own specs.
# feature
Scenario: create resource from one time request
Given I am an admin
When I make an authenticated request for a new resource
Then I am redirected
And I see the message "Resource successfully created"
# step definitions using Rack::Test
When /^I make an authenticated request for a new resource$/ do
post resources_path, :auth_token => @admin.authentication_token
follow_redirect!
end
Then /^I am redirected$/ do
last_response.should_not be_redirect
last_request.env["HTTP_REFERER"].should include(resources_path)
end
Then /^I see the message "([^"]*)"$/ do |msg|
last_response.body.should include(msg)
end
回答6:
Although, not an exact answer to the question, the best solution for me has been to use Capybara for specs that simulate user interaction (using visit
), and Rack Test for test API like requests. They can be used together within the same test suite.
Adding the following to the spec helper gives access to get
, post
and other Rack test methods:
RSpec.configure do |config|
config.include Rack::Test::Methods
You may need to put the Rack Test specs in a spec/requests
folder.
回答7:
With an application using RSpec 3+, you would not want to make an HTTP POST request with Capybara. Capybara is for emulating user behavior, and accepting the JS behavior and page content that results. An end user doesnt form HTTP POST requests for resources in your application, a user clicks buttons, clicks ajax links, drags n drops elements, submits web forms, etc.
Check out this blog post on Capybara and other HTTP methods. The author makes the following claim:
Did you see any mention of methods like get, post or response? No? That’s because those don’t exist in Capybara. Let’s be very clear about this...Capybara is not a library suited to testing APIs. There you have it. Do not test APIs with Capybara. It wasn’t designed for it.
So, developing an API or not, if you have to make an explicit HTTP POST request, and it does not involve an HTML element and some sort of event (click, drag, select, focusout, whatever), then it shouldn't be tested with Capybara. If you can test the same feature by clicking some button, then do use Capybara.
What you likely want is RSpec Request specs. Here you can make post
calls, and any other HTTP method as well, and assert expectations on the response. You can also mock n stub objects and methods to assert expectations in regards to side effects and other behaviors that happen in between your request and the response.
# spec located in spec/requests/project_file_upload_spec.rb
require "rails_helper"
RSpec.describe "Project File Upload", type: :request do
let(:project) { create(:project) }
let(:file) { File.new(File.join(::Rails.root.to_s, 'path/to/file.ext')) } # can probably extract this to a helper...
it "accepts a file uploaded to a Project resource" do
post "project/#{project.id}/upload", upload_path: file
expect(response).to be_success
expect(project.file?).to eq(true)
# expect(project.file).not_to eq(nil)
expect(response).to render_template(:show)
end
end
来源:https://stackoverflow.com/questions/4084212/how-do-you-post-to-a-url-in-capybara