How do you POST to a URL in Capybara?
Solution 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
Solution 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.
Solution 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
Solution 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.
Solution 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
Comments
-
Clinton about 4 years
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?
-
Abhinav Kaushal Keshari over 13 yearsThis sounds right. I have a situation where I want to make sure users with a specific permission cannot POST to a URL. So I guess what you're saying is Capybara+Cucumber should not be used for that.
-
Ramon Tayag almost 13 yearsUnfortunately I'm getting
undefined method 'post' for #<Capybara::Selenium::Driver:0x3f75e10> (NoMethodError)
. -
Clinton over 12 yearsLooks like the latest versions of capybara break this -- my gemfile currently restricts to an earlier version: gem 'capybara', "0.4.1.2"
-
Zubin over 12 yearsAlthough it's not required for your example, it's better to use
page.driver.post
so it's in the same context as other steps. -
B Seven over 11 yearsYep, this happened for me too. Used to work on another computer a few months ago. I guess running an earlier version will fix it.
-
evedovelli over 10 yearsIt may be useful to test a POST. For example, if can use it to test your code is resilient against bots trying to post things where they should not be allowed to.
-
Jaco Pretorius over 7 yearsI get
undefined method
submit' for #<Capybara::Poltergeist::Driver:0x007f8cdcc699c0>` - Capybara 2.9.0 -
Todd about 7 yearsDont use Capybara for making HTTP POST requests. Its designed to emulate a user using a web browser. The user doesnt form HTTP POST requests, the user clicks buttons and links and submits forms and such. Use RSpec Request specs for testing responses from POST, PUT, DELETE requests. Check out this blog post: matthewlehner.net/rails-api-testing-guidelines
-
jwadsack almost 7 yearsThis seems to be the most up-to-date response and works with Webkit driver as well (which doesn't have the
post
method) -
Todd over 6 yearsThis seems like an unconventional use of Capybara, and while it may allow the OP to achieve desired functionality, it may induce an antipattern into the test suite. RSpec Request specs are much better suited to testing the behavior of an HTTP POST to an application.
-
Luke Rohde almost 6 yearsThis is beautiful - needed to post though the api, to see something happen in the user's browser via websockets. THANKS!
-
oliverguenther over 5 yearsThe response object returned by post is just the status code, you can access the response object with
session.response
afterwards, which also has the #body method. Together with this piece of information, POSTing outside Capybara works perfectly! -
XtraSimplicity about 5 yearsIt's not that simple, anymore, as Rack::Test::Methods requires the instance method
app
to return an instance of the Rack application. github.com/rack-test/rack-test/blob/master/lib/rack/test/… -
jangosteve about 5 yearsIn my case, the user actually does submit an HTTP request from their command line, which returns some data, including a unique URL, which they then visit in their browser. The integration test I need would perform a GET request, receive data including a URL, and then follows the URL in the browser and ensures the displayed data in the browser matches the data returned by the GET request. The problem with blanket statements like, "The user doesn't..." is that they assume you know what everyone's users do.
-
Arnaud Meuret over 4 years@jangosteve Todd merely said that Capybara was designed to emulate a user using a browser. Your users do not use a browser, do not expect Capybara to be able mimic them.
-
Ian Vaughan over 4 yearsIts needed when testing 3rd party callbacks that make post requests.