How do you POST to a URL in Capybara?

前端 未结 8 611
无人及你
无人及你 2020-11-28 09:51

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 ste

相关标签:
8条回答
  • 2020-11-28 10:11

    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.

    0 讨论(0)
  • 2020-11-28 10:11

    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.

    0 讨论(0)
  • 2020-11-28 10:13

    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
    
    0 讨论(0)
  • 2020-11-28 10:14

    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
    
    0 讨论(0)
  • 2020-11-28 10:16

    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
    
    0 讨论(0)
  • 2020-11-28 10:19

    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
    0 讨论(0)
提交回复
热议问题