Как вы отправляете сообщение на URL-адрес в Capybara?

Только что переключился с Cucumber + Webrat на Cucumber + Capybara, и мне интересно, как вы можете отправить контент по URL-адресу в Capybara.

В Cucumber+Webrat мне удалось сделать шаг:

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

Однако в документации Capybara упоминается:

Метод посещения принимает только один параметр, метод запроса всегда GET.always GET.

Как мне изменить свой шаг, чтобы Cucumber + Capybara выполнял POST для URL-адреса?


person Clinton    schedule 03.11.2010    source источник
comment
Почему бы не заполнить свой файл и не отправить его. В сценарии с огурцом вам нужно быть похожим на пользователя и делать только клик за кликом. Пользователь не может создать метод публикации без какой-либо формы.   -  person shingara    schedule 03.11.2010
comment
@shingara Спасибо за предложение, я немного злоупотреблял огурцом, так как для этого конкретного действия нет формы. Для разных клиентов (программного обеспечения) выгружать содержимое в приложение. Я хочу имитировать клиентов, загружающих данные в приложение.   -  person Clinton    schedule 03.11.2010
comment
использование капибары не очень хорошо, если вы не используете какой-либо javascript.   -  person shingara    schedule 03.11.2010
comment
@shingara - Это интересная идея, попытаться опубликовать данные через javascript?   -  person Clinton    schedule 03.11.2010
comment
нет перехода с webrat на капибару, только если вам действительно нужно протестировать какой-нибудь javascript в вашем коде. Если вы этого не сделали, это не лучший выбор для миграции.   -  person shingara    schedule 03.11.2010
comment
Вы можете попробовать добавить форму где-нибудь в своем приложении, чтобы выполнить этот почтовый запрос. И использовать его, чтобы сделать свой пост.   -  person shingara    schedule 03.11.2010
comment
у меня работает: stackoverflow.com/a/7272860/316700   -  person fguillen    schedule 30.04.2013
comment
Для скептиков: хотя пользователь не должен публиковать сообщения без формы, это возможно; Я обнаружил ситуацию, когда ответный удар заставляет Chrome повторять ошибку POST. Естественно, я хочу это проверить. (И, конечно же, пользователь может использовать curl.) Иногда нам нужно опустить уровень абстракции.   -  person Nathan Long    schedule 22.08.2014


Ответы (8)


Совсем недавно я нашел эту замечательную запись в блоге. Это отлично подходит для таких случаев, как Тони, и когда вы действительно хотите опубликовать что-то в своем кьюке:

Для моего случая это стало:

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
person Clinton    schedule 11.04.2011
comment
К сожалению, я получаю undefined method 'post' for #<Capybara::Selenium::Driver:0x3f75e10> (NoMethodError). - person Ramon Tayag; 27.07.2011
comment
Похоже, что последние версии водосвинки нарушают это - мой gemfile в настоящее время ограничивается более ранней версией: gem 'capybara', 0.4.1.2 - person Clinton; 09.09.2011
comment
Да, это случилось и со мной. Несколько месяцев назад работал на другом компьютере. Я думаю, запуск более ранней версии исправит это. - person B Seven; 24.10.2012

Вы можете сделать это:

rack_test_session_wrapper = Capybara.current_session.driver
rack_test_session_wrapper.submit :post, your_path, nil
  • Вы можете заменить :post каким бы то ни было методом, который вам нужен, например. :put или :delete.
  • Замените your_path на путь Rails, который вы хотите, например. rack_test_session_wrapper.submit :delete, document_path(Document.last), nil удалит последний документ в моем приложении.
person Mike    schedule 08.03.2012
comment
Я получаю undefined method submit' для #‹Capybara::Poltergeist::Driver:0x007f8cdcc699c0›` - Capybara 2.9.0 - person Jaco Pretorius; 28.09.2016
comment
Не используйте Capybara для отправки HTTP-запросов POST. Он предназначен для имитации пользователя, использующего веб-браузер. Пользователь не формирует запросы HTTP POST, пользователь нажимает кнопки и ссылки, отправляет формы и тому подобное. Используйте спецификации запроса RSpec для тестирования ответов на запросы POST, PUT, DELETE. Ознакомьтесь с этим сообщением в блоге: matthewlehner.net/rails-api-testing-guidelines - person Todd; 09.04.2017
comment
В моем случае пользователь фактически делает отправку HTTP-запроса из своей командной строки, который возвращает некоторые данные, включая уникальный URL-адрес, который он затем посещает в своем браузере. Интеграционный тест, который мне нужен, будет выполнять запрос GET, получать данные, включая URL-адрес, а затем следовать URL-адресу в браузере и обеспечивать соответствие отображаемых данных в браузере данным, возвращенным запросом GET. Проблема с общими утверждениями типа «Пользователь не...» заключается в том, что они предполагают, что вы знаете, что делают все пользователи. - person jangosteve; 20.03.2019
comment
@jangosteve Тодд просто сказал, что Capybara была разработана для эмуляции пользователя, использующего браузер. Ваши пользователи не используют браузер, не ожидайте, что Capybara сможет их имитировать. - person Arnaud Meuret; 17.10.2019

Если у вашего драйвера нет post (например, у Полтергейста), вы можете сделать это:

session = ActionDispatch::Integration::Session.new(Rails.application)
response = session.post("/mypath", my_params: "go_here")

Но обратите внимание, что этот запрос происходит в новом сеансе, поэтому вам придется пройти через объект response, чтобы утвердить его.

Как было сказано в другом месте, в тесте Capybara вы обычно хотите выполнять POST, отправляя форму так же, как пользователь. Я использовал приведенное выше, чтобы проверить, что происходит с пользователем, если POST происходит в другом сеансе (через WebSockets), поэтому форма не обрезает его.

Документы:

person Henrik N    schedule 22.06.2014
comment
Кажется, это самый актуальный ответ, который также работает с драйвером Webkit (у которого нет метода post) - person jwadsack; 11.07.2017
comment
Это красиво - нужно опубликовать через API, чтобы увидеть, что что-то происходит в браузере пользователя через веб-сокеты. БЛАГОДАРНОСТЬ! - person Luke Rohde; 11.07.2018
comment
Объект ответа, возвращаемый почтой, — это просто код состояния, после чего вы можете получить доступ к объекту ответа с помощью session.response, который также имеет метод #body. Вместе с этой информацией POSTing вне Capybara работает отлично! - person oliverguenther; 11.01.2019

Capybara visit выполняет только запросы GET. Это по дизайну.

Чтобы пользователь выполнил POST, он должен нажать кнопку или отправить форму. Другого способа сделать это с помощью браузера нет.

Правильный способ проверить это поведение:

visit "project/:id/edit" # This will only GET
attach_file "photo", File.open('cute_photo.jpg')
click_button 'Upload' # This will POST

Если вы хотите протестировать API, я рекомендую использовать spec/request вместо огурца, но это только я.

person Ariejan    schedule 03.11.2010
comment
Это звучит правильно. У меня есть ситуация, когда я хочу убедиться, что пользователи с определенным разрешением не могут отправить POST на URL-адрес. Итак, я думаю, вы говорите, что Capybara + Cucumber не следует использовать для этого. - person Tony; 20.12.2010
comment
Может быть полезно протестировать POST. Например, если вы можете использовать его для проверки устойчивости вашего кода к ботам, пытающимся публиковать вещи там, где им не должно быть разрешено. - person evedovelli; 01.11.2013
comment
Это необходимо при тестировании сторонних обратных вызовов, которые делают почтовые запросы. - person Ian Vaughan; 22.01.2020

Я знаю, что ответ уже принят, но я хотел бы предоставить обновленный ответ. Вот метод от Энтони Идена и Corey Haines, который передает Rack::Test объекту Cucumber's World:

Тестирование REST API с Cucumber и Rack:: Тест

С помощью этой техники я смог напрямую отправлять почтовые запросы в определениях шагов. При написании определений шагов было чрезвычайно полезно изучить Rack::Test API из его собственного спецификации.

# 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
person simeonwillbanks    schedule 31.10.2011
comment
Хотя это и не требуется для вашего примера, лучше использовать page.driver.post, чтобы он находился в том же контексте, что и другие шаги. - person Zubin; 02.12.2011

Хотя это не точный ответ на вопрос, лучшим решением для меня было использовать Capybara для спецификаций, которые имитируют взаимодействие с пользователем (с использованием visit), и Rack Test для тестовых API, таких как запросы. Их можно использовать вместе в одном наборе тестов.

Добавление следующего в помощник спецификации дает доступ к get, post и другим методам тестирования Rack:

RSpec.configure do |config|
  config.include Rack::Test::Methods

Возможно, вам потребуется поместить спецификации Rack Test в папку spec/requests.

person B Seven    schedule 27.05.2015
comment
Это похоже на нетрадиционное использование Capybara, и, хотя это может позволить OP достичь желаемой функциональности, это может вызвать антипаттерн в наборе тестов. Спецификации запросов RSpec гораздо лучше подходят для тестирования поведения HTTP POST в приложении. - person Todd; 20.12.2017
comment
Это уже не так просто, поскольку Rack::Test::Methods требует, чтобы метод экземпляра app возвращал экземпляр приложения Rack. github.com/rack-test/ стеллаж-тест/блоб/мастер/библиотека/стойка/тест/ - person XtraSimplicity; 12.03.2019

С приложением, использующим RSpec 3+, вы не захотите делать запрос HTTP POST с помощью Capybara. Capybara предназначена для имитации поведения пользователя и принятия поведения JS и содержимого страницы в результате. Конечный пользователь не формирует HTTP-запросы POST для ресурсов в вашем приложении, пользователь нажимает кнопки, нажимает ссылки ajax, перетаскивает элементы, отправляет веб-формы и т. д.

Прочтите эту запись в блоге о Capybara и других методах HTTP. Автор делает следующее утверждение:

Вы видели какие-либо упоминания о таких методах, как get, post или response? Нет? Это потому, что их нет в Capybara. Давайте проясним это... Capybara — это не библиотека, подходящая для тестирования API. Вот оно. Не тестируйте API с помощью Capybara. Он не был предназначен для этого.

Таким образом, разрабатывается API или нет, если вам нужно сделать явный запрос HTTP POST, и он не включает элемент HTML и какое-то событие (щелчок, перетаскивание, выбор, выделение фокуса и т. д.), то это не должно быть проверено с капибарой. Если вы можете протестировать ту же функцию, нажав какую-либо кнопку, используйте Capybara.

Скорее всего, вам нужны спецификации запроса RSpec. . Здесь вы можете делать post вызовы, а также любые другие HTTP-методы и утверждать ожидания в отношении ответа. Вы также можете имитировать n объектов-заглушек и методов, чтобы подтвердить ожидания в отношении побочных эффектов и других действий, которые происходят между вашим запросом и ответом.

# 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
person Todd    schedule 08.04.2017

Как уже говорили другие, нет прямого способа выполнить POST с Capybara, потому что все дело во взаимодействии с браузером. Для тестирования API я очень настоятельно рекомендую гем rspec_api_documentation.

person Marnen Laibow-Koser    schedule 22.04.2020