What if WebMock and NSURLProtocol had a baby?
Features
- Supports most any HTTP library that is built on NSURLConnection / NSURLSession
- Request matching based upon HTTP method, URI, and body
- Optionally, disable real network access
- Familiar, delicious syntax
- Bacon integration
Installation
Update your Gemfile:
gem "webstub"
Bundle:
$ bundle install
Usage
-
Add the following line to the top-most
describe
block in your spec:extend WebStub::SpecHelpers
-
Use the following methods to control the use of request stubbing:
disable_network_access!
enable_network_access!
stub_request
reset_stubs
Example Spec
describe "Example" do
extend WebStub::SpecHelpers
describe "Stubbing a GET request to return a simple response after a delay" do
it "retrieves the front page" do
stub_request(:get, "http://example.com/").
to_return(body: "Hello!", content_type: "text/plain", delay: 0.3)
@body = nil
@api.get_index do |body, error|
@body = body
resume
end
wait_max 1.0 do
@body.should.be == "Hello!"
end
end
end
describe "Stubbing a GET request to return JSON" do
it "retrieves suggestions" do
stub_request(:get, "https://example.com/suggestions?q=mu").
to_return(json: { suggestions: ["muse"] })
@suggestions = nil
@api.get_suggestions("mu") do |results, error|
@suggestions = results
resume
end
wait_max 1.0 do
@suggestions.should.not.be.empty
end
end
end
describe "Stubbing a POST request to return JSON" do
it "handles a POST request" do
stub_request(:post, "https://example.com/action").
with(body: { q: "unsustainable" }).
to_return(json: [ { album: "The 2nd Law", release_date: "2012-10-01", artist: "Muse" } ])
@results = nil
@api.get_album_info_for_track("unsustainable") do |results, error|
@results = results
resume
end
wait_max 1.0 do
@results.should.not.be.empty
end
end
end
describe "Stubbing a GET request to fail" do
it "returns an NSError with the NSURLError domain" do
stub_request(:get, "https://example.com/action").
to_fail(code: NSURLErrorNotConnectedToInternet)
@error = nil
@api.get_albums do |results, error|
@error = error
resume
end
wait_max 1.0 do
@error.code.should == NSURLErrorNotConnectedToInternet
end
end
end
end
Conventions
- The URL is matched exactly as is right now (hence query parameters need to be included and encoded)
- The
with
method'sbody
option accepts either a Hash or a String:- Hashes are assumed to be form data (with a
application/x-www-form-urlencoded
content type) - Strings are matched as is
- Hashes are assumed to be form data (with a
- The
to_return
method accepts a few options:-
json
: accepts either a Hash, Array or a String. If a Hash or Array is provided, it will be converted to JSON. Strings are returned as is, with the Content-Type set toapplication/json
. -
body
: accepts a String, and returns it as-is -
content_type
: sets the Content-Type when using thebody
parameter -
status_code
: sets the integer Status Code of the response. Defaults to200
.
-
- The
to_redirect
method accepts:-
url
: String of the URL to redirect to (required) - All options supported by
to_return
except forstatus_code
-
- The
to_fail
method accepts one of the following options:-
code
:NSURLErrorDomain
error code -
error
:NSError
to fail the request with
-
Expectations
Sometimes, you may just want to check that the request has been made to a given URL. In this case, you can use the requested?
method of the stub returned by stub_request
:
describe Elevate::HTTP do
extend WebStub::SpecHelpers
describe ".get" do
it "synchronously issues a HTTP GET request" do
stub = stub_request(:get, "http://www.example.com/")
Elevate::HTTP.get("http://www.example.com/")
stub.should.be.requested
end
end
end
Callbacks
Sometimes, you may want to inspect the request made for a given URL. In this case, you can use the with_callback
method of the stub returned by stub_request
to add a callback hook:
describe Elevate:HTTP do
extend WebStub::SpecHelpers
describe ".post" do
it "synchronously issues an HTTP POST request" do
stub = stub_request(:post, "http://www.example.com/")
stub.with_callback do |headers, body|
headers.kind_of?(Hash).should == true
body.kind_of?(Hash).should == true
body.should == {"key" => "value"}
end
Elevate::HTTP.post("http://www.example.com/", json: {key: "value"})
end
end
end
Caveats
While WebStub supports NSURLSession
, it does not support background sessions, as they don't allow the use of custom NSURLProtocol
classes.
TODO
- Handle query params similarly to form data