一个重写 Web 代理,用于测试浏览器与外部站点之间的交互。 与 ruby + rspec 兼容。
A rewriting web proxy for testing interactions between your browser and external sites. Works with ruby + rspec.
Puffing Billy is like webmock or VCR, but for your browser.
Billy spawns an EventMachine-based proxy server, which it uses to intercept requests sent by your browser. It has a simple API for configuring which requests need stubbing and what they should return.
Billy lets you test against known, repeatable data. It also allows you to test for failure cases. Does your twitter (or facebook/google/etc) integration degrade gracefully when the API starts returning 500s? Well now you can test it!
it 'should stub google' do
proxy.stub('http://www.google.com/').and_return(text: "I'm not Google!")
visit 'http://www.google.com/'
expect(page).to have_content("I'm not Google!")
end
You can also record HTTP interactions and replay them later. See caching below.
Add this line to your application's Gemfile:
gem 'puffing-billy', group: :test
And then execute:
$ bundle
Or install it yourself as:
$ gem install puffing-billy
In your rails_helper.rb:
require 'billy/capybara/rspec'
# select a driver for your chosen browser environment
Capybara.javascript_driver = :selenium_billy # Uses Firefox
# Capybara.javascript_driver = :selenium_headless_billy # Uses Firefox in headless mode
# Capybara.javascript_driver = :selenium_chrome_billy
# Capybara.javascript_driver = :selenium_chrome_headless_billy
# Capybara.javascript_driver = :apparition_billy
# Capybara.javascript_driver = :webkit_billy
# Capybara.javascript_driver = :poltergeist_billy
# Capybara.javascript_driver = :cuprite_billy
Note:
:poltergeist_billydoesn't support proxying any localhosts, so you must use:webkit_billy,:apparition_billy, or a custom headless selenium registration for headless specs when using puffing-billy for other local rack apps. See this phantomjs issue for any updates.
In your rails_helper.rb:
require 'billy/watir/rspec'
# select a driver for your chosen browser environment
@browser = Billy::Browsers::Watir.new :firefox
# @browser = Billy::Browsers::Watir.new = :chrome
# @browser = Billy::Browsers::Watir.new = :phantomjs
An example feature:
Feature: Stubbing via billy
@javascript @billy
Scenario: Test billy
And a stub for google
In your features/support/env.rb:
require 'billy/capybara/cucumber'
After do
Capybara.use_default_driver
end
And in steps:
Before('@billy') do
Capybara.current_driver = :poltergeist_billy
end
And /^a stub for google$/ do
proxy.stub('http://www.google.com/').and_return(text: "I'm not Google!")
visit 'http://www.google.com/'
expect(page).to have_content("I'm not Google!")
end
It's good practice to reset the driver after each scenario, so having an
@billy tag switches the drivers on for a given scenario. Also note that
stubs are reset after each step, so any usage of a stub should be in the
same step that it was created in.
In your features/support/env.rb:
require 'billy/watir/cucumber'
After do
@browser.close
end
And in steps:
Before('@billy') do
@browser = Billy::Browsers::Watir.new :firefox
end
And /^a stub for google$/ do
proxy.stub('http://www.google.com/').and_return(text: "I'm not Google!")
@browser.goto 'http://www.google.com/'
expect(@browser.text).to eq("I'm not Google!")
end
In the case you are using a Chrome instance, running on another machine, or in another Docker container, you need to :
--proxy-server=:Puffing billy doesn't support websockets, so if you are using them,
or ActionCable for the Ruby On Rails developers, you can tell Chrome to bypass
the proxy for websockets by adding the flag --proxy-bypass-list=ws://* to
your remote chrome intance or Docker container.
Please see this link for details and report back to Issue #49 if you get it fully working.
…
Stubs are reset between tests. Any requests that are not stubbed will be proxied to the remote server.
If for any reason you'd need to reset stubs manually you can do it in two ways:
# reset a single stub
example_stub = proxy.stub('http://example.com/text/').and_return(text: 'Foobar')
proxy.unstub example_stub
# reset all stubs
proxy.reset
Requests routed through the external proxy are cached.
By default, all requests to localhost or 127.0.0.1 will not be cached. If you're running your test server with a different hostname, you'll need to add that host to puffing-billy's whitelist.
In your rails_helper.rb:
…
The handler column indicates how Puffing Billy handled your request:
If your status is set to inflight this request has not yet been handled fully. Either puffing billy crashed
internally on this request, or your test ended before it could complete successfully.
If you need to cache different responses to the same HTTP request, you can use cache scoping.
For example, an index page may return zero or more items in a list, with or without pagination, depending on the number of entries in a database.
There are a few different ways to use cache scopes:
…
If you use named caches it is highly recommend that you use a global hook to set the cache back to the default before or after each test.
In Rspec:
RSpec.configure do |config|
config.before :each { proxy.cache.use_default_scope }
end
If you want the cache for each test to be independent, i.e. have it's own directory where the cache files are stored, you can do so.
use a Before tag:
Before('@javascript') do |scenario, block|
Billy.configure do |c|
feature_name = scenario.feature.name.underscore
scenario_name = scenario.name.underscore
c.cache_path = "features/support/fixtures/req_cache/#{feature_name}/#{scenario_name}/"
FileUtils.mkdir_p(Billy.config.cache_path) unless File.exist?(Billy.config.cache_path)
end
end
use a before(:each) block:
…
If you want to record requests to stubbed URIs, set the following configuration option:
Billy.configure do |c|
c.record_stub_requests = true
end
Example usage:
it 'should intercept a GET request' do
stub = proxy.stub('http://example.com/')
visit 'http://example.com/'
expect(stub.has_requests?).to be true
expect(stub.requests).not_to be_empty
end
By default, the Puffing Billy proxy will use the EventMachine::HttpRequest timeouts of 5 seconds
for connect and 10 seconds for inactivity when talking to downstream servers.
These can be configured as follows:
Billy.configure do |c|
c.proxied_request_connect_timeout = 20
c.proxied_request_inactivity_timeout = 20
end
If you use a customised Capybara driver, remember to set the proxy address and tell it to ignore SSL certificate warnings. See lib/billy.rb to see how Billy's default drivers are configured.
If you use VCR and Webmock elsewhere in your specs, you may need to disable them
for your specs utilizing Puffing Billy. To do so, you can configure your rails_helper.rb
as shown below:
RSpec.configure do |config|
config.around(:each, type: :feature) do |example|
WebMock.allow_net_connect!
VCR.turned_off { example.run }
WebMock.disable_net_connect!
end
end
As an alternative if you're using VCR, you can ignore requests coming from the browser.
One way of doing that is by adding to your rails_helper.rb the excerpt below:
VCR.configure do |config|
config.ignore_request do |request|
request.headers.include?('Referer')
end
end
Note that this approach may cause unexpected behavior if your backend sends the Referer HTTP header (which is unlikely).
By default Puffing Billy suppresses errors from stub-blocks. To make it raise errors instead, add this test initializers:
EM.error_handler { |e| raise e }
Unfortunately we cannot setup the runtime certificate authority on your browser at time of configuring the Capybara driver. So you need to take care of this step yourself as a prepartion. A good point would be directly after configuring this gem.
Google Chrome/Chromium is capable to run as a test browser with the new
headless mode which is not able to handle the deprecated
--ignore-certificate-errors flag. But the headless mode is capable of
handling the user PKI certificate store. So you just need to import the
runtime Puffing Billy certificate authority on your system store, or generate a
new store for your current session. The following examples demonstrates the
former variant:
…
Mind the reset of the HOME environment variable. Fortunately Chrome takes
care of the users home, so we can setup a new temporary directory for the test
run, without messing with potential user configurations.
The macOS support requires the input of your password to manipulate the system certificate store. If you are lazy you can turn off sudo password prompt for the security command, but it's strongly advised against. (You know passwordless security, is no security in this case) Further, the macOS handling here cleans up old Puffing Billy root certificate authorities and put the current one into the system store. So after a run of your the suite only one certificate will be left over. If this is not enough you can handling the cleanup again with a custom on-after hook.
em-http-request was modified to emit a warning if being used without the TLS
verify_peer option. Puffing Billy defaults to specifying verify_peer: false
but you can now modify configuration to do peer verification. So if you've
gone to the trouble of setting up your own certificate authority and self-signed
certs you can enable it like so:
Billy.configure do |c|
c.verify_peer = true
end
git checkout -b my-new-feature)git commit -am 'Added some feature')git push origin my-new-feature)暂无开放 Issues,或尚未同步最近议题。