├── .travis.yml
├── lib
├── mock5
│ ├── version.rb
│ └── api.rb
└── mock5.rb
├── Rakefile
├── Gemfile
├── .gitignore
├── mock5.gemspec
├── LICENSE.txt
├── spec
├── mock5_api_spec.rb
└── mock5_spec.rb
└── README.md
/.travis.yml:
--------------------------------------------------------------------------------
1 | rvm:
2 | - 1.9
3 | - 2.0
4 | - 2.1
5 | - 2.2
6 |
--------------------------------------------------------------------------------
/lib/mock5/version.rb:
--------------------------------------------------------------------------------
1 | module Mock5
2 | VERSION = "1.0.8".freeze
3 | end
4 |
--------------------------------------------------------------------------------
/Rakefile:
--------------------------------------------------------------------------------
1 | require "bundler/gem_tasks"
2 | require "rspec/core/rake_task"
3 |
4 | RSpec::Core::RakeTask.new
5 |
6 | task default: :spec
7 |
--------------------------------------------------------------------------------
/Gemfile:
--------------------------------------------------------------------------------
1 | source "https://rubygems.org"
2 |
3 | # Specify your gem's dependencies in mock5.gemspec
4 | gemspec
5 |
6 | gem "bundler", "~> 1.6"
7 | gem "rspec", "~> 3.1"
8 | gem "rake", "~> 10.3"
9 | gem "pry"
10 |
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | *.gem
2 | *.rbc
3 | .bundle
4 | .config
5 | .yardoc
6 | Gemfile.lock
7 | InstalledFiles
8 | _yardoc
9 | coverage
10 | doc/
11 | lib/bundler/man
12 | pkg
13 | rdoc
14 | spec/reports
15 | test/tmp
16 | test/version_tmp
17 | tmp
18 | .ruby-*
19 |
--------------------------------------------------------------------------------
/mock5.gemspec:
--------------------------------------------------------------------------------
1 | # coding: utf-8
2 | require File.expand_path("../lib/mock5/version", __FILE__)
3 |
4 | Gem::Specification.new do |spec|
5 | spec.name = "mock5"
6 | spec.version = Mock5::VERSION
7 | spec.authors = ["Pavel Pravosud"]
8 | spec.email = ["pavel@pravosud.com"]
9 | spec.summary = "Mock APIs using Sinatra"
10 | spec.description = "Create and manage API mocks with Sinatra"
11 | spec.homepage = "https://github.com/rwz/mock5"
12 | spec.license = "MIT"
13 | spec.files = Dir["LICENSE.text", "README.md", "lib/**/**"]
14 | spec.require_path = "lib"
15 | spec.required_ruby_version = ">= 1.9.3"
16 |
17 | spec.add_dependency "webmock", "~> 1.15"
18 | spec.add_dependency "sinatra", "~> 1.4"
19 | end
20 |
--------------------------------------------------------------------------------
/LICENSE.txt:
--------------------------------------------------------------------------------
1 | Copyright (c) 2014 Pavel Pravosud
2 |
3 | MIT License
4 |
5 | Permission is hereby granted, free of charge, to any person obtaining
6 | a copy of this software and associated documentation files (the
7 | "Software"), to deal in the Software without restriction, including
8 | without limitation the rights to use, copy, modify, merge, publish,
9 | distribute, sublicense, and/or sell copies of the Software, and to
10 | permit persons to whom the Software is furnished to do so, subject to
11 | the following conditions:
12 |
13 | The above copyright notice and this permission notice shall be
14 | included in all copies or substantial portions of the Software.
15 |
16 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
17 | EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
18 | MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
19 | NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
20 | LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
21 | OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
22 | WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
23 |
--------------------------------------------------------------------------------
/spec/mock5_api_spec.rb:
--------------------------------------------------------------------------------
1 | require "mock5/api"
2 | require "rack/mock"
3 |
4 | describe Mock5::Api do
5 | describe "#endpoint" do
6 | it "matches all by default" do
7 | expect(subject.endpoint).to eq(/.*/)
8 | end
9 |
10 | it "can be specified as a regex" do
11 | api = described_class.new(/foo/)
12 | expect(api.endpoint).to eq(/foo/)
13 | end
14 |
15 | it "can be specified as a valid url without path" do
16 | api = described_class.new("http://example.com")
17 | expect(api.endpoint).to eq(%r(\Ahttp://example\.com/.*\z))
18 | end
19 |
20 | it "can not be specified as a valid url with path" do
21 | expect{ described_class.new("http://example.com/foo") }
22 | .to raise_error(ArgumentError, "Endpoint URL should not include path")
23 | end
24 |
25 | it "can not be specified as an invalid url string" do
26 | expect{ described_class.new("foo") }
27 | .to raise_error(ArgumentError, "Endpoint should be a valid URL")
28 | end
29 |
30 | it "can not be specified by anything else" do
31 | [false, :foo, 123].each do |invalid_endpoint|
32 | expect{ described_class.new(invalid_endpoint) }
33 | .to raise_error(ArgumentError, "Endpoint should be string or regexp")
34 | end
35 | end
36 | end
37 |
38 | describe "#app" do
39 | it "is a Class" do
40 | expect(subject.app).to be_kind_of(Class)
41 | end
42 |
43 | it "is a Sinatra Rack app" do
44 | expect(subject.app.superclass).to eq(Sinatra::Base)
45 | end
46 |
47 | describe "configuration" do
48 | subject do
49 | described_class.new do
50 | get "/hello/:what" do |what|
51 | "Hello, #{what.capitalize}"
52 | end
53 | end
54 | end
55 |
56 | let(:server){ Rack::Server.new(app: subject.app) }
57 | let(:mock_request){ Rack::MockRequest.new(server.app) }
58 |
59 | it "can be configures by a block" do
60 | response = mock_request.get("/hello/world")
61 | expect(response.body.to_s).to eq("Hello, World")
62 | end
63 | end
64 | end
65 |
66 | describe "#request_stub" do
67 | it "returns a request stub" do
68 | expect(subject.request_stub).to be_kind_of(WebMock::RequestStub)
69 | end
70 | end
71 | end
72 |
--------------------------------------------------------------------------------
/lib/mock5/api.rb:
--------------------------------------------------------------------------------
1 | require "uri"
2 | require "sinatra"
3 | require "webmock"
4 |
5 | module Mock5
6 | # A class representing an API mock
7 | class Api
8 |
9 | # @return [Sinatra::Base] a Sinatra app mocking the API
10 | attr_reader :app
11 |
12 | # @return [Regexp] a regexp to match the API request urls
13 | attr_reader :endpoint
14 |
15 | # Returns an instance of +Mock5::Api+
16 | #
17 | # @example
18 | # my_mock_api = Mock5::Api.new("http://example.com") do
19 | # get "posts" do
20 | # [
21 | # {id: 1, body: "a posy body"},
22 | # {id: 2, body: "another post body"}
23 | # ].to_json
24 | # end
25 | #
26 | # post "posts" do
27 | # halt 201, "The post was created successfully"
28 | # end
29 | # end
30 | #
31 | # @param endpoint [String, Regexp] a url of the API service to
32 | # endpoint to mock. Can only contain schema and hostname, path
33 | # should be empty.
34 | #
35 | # @yield a block passed to Sinatra to initialize an app
36 | #
37 | # @return [Mock5::Api]
38 | def initialize(endpoint=nil, &block)
39 | @app = Sinatra.new(&block)
40 | @endpoint = normalize_endpoint(endpoint)
41 | end
42 |
43 | # Returns webmock request stub built with Sinatra app and enpoint url
44 | #
45 | # @return [WebMock::RequestStub]
46 | def request_stub
47 | @request_stub ||= WebMock::RequestStub.new(:any, endpoint).tap{ |s| s.to_rack(app) }
48 | end
49 |
50 | private
51 |
52 | def normalize_endpoint(endpoint)
53 | case endpoint
54 | when nil
55 | /.*/
56 | when String
57 | normalize_string_endpoint(endpoint)
58 | when Regexp
59 | endpoint
60 | else
61 | raise ArgumentError, "Endpoint should be string or regexp"
62 | end
63 | end
64 |
65 | def normalize_string_endpoint(endpoint)
66 | uri = URI.parse(endpoint)
67 |
68 | if uri.scheme !~ /\Ahttps?/
69 | raise ArgumentError, "Endpoint should be a valid URL"
70 | elsif uri.path != ?/ && !uri.path.empty?
71 | raise ArgumentError, "Endpoint URL should not include path"
72 | end
73 |
74 | uri.path = ""
75 | endpoint = Regexp.escape(uri.to_s)
76 |
77 | Regexp.new("\\A#{endpoint}\/#{app_paths_regex}\\z")
78 | end
79 |
80 | def app_paths_regex
81 | regexes = app.routes.values.flatten.select{ |v| Regexp === v }
82 | paths = regexes.map{ |regex| regex.source[3..-3] }
83 |
84 | return ".*" if paths.empty?
85 |
86 | paths = paths.one?? paths.first : %{(?:#{paths.join("|")})}
87 |
88 | "#{paths}(?:\\?.*)?"
89 | end
90 | end
91 | end
92 |
--------------------------------------------------------------------------------
/lib/mock5.rb:
--------------------------------------------------------------------------------
1 | require "set"
2 |
3 | # The main module of the gem, exposing all API management methods.
4 | # Can be included into class.
5 | module Mock5
6 | extend self
7 |
8 | autoload :VERSION, "mock5/version"
9 | autoload :Api, "mock5/api"
10 |
11 | # Returns a set of currently mounted APIs
12 | #
13 | # @return [Set] a list of currently mounted APIs
14 | def mounted_apis
15 | @_mounted_apis ||= Set.new
16 | end
17 |
18 | # Generates a new API
19 | #
20 | # @example
21 | # my_mock_api = Mock5.mock("http://example.com") do
22 | # get "posts" do
23 | # [
24 | # {id: 1, body: "a post body"},
25 | # {id: 2, body: "another post body"}
26 | # ].to_json
27 | # end
28 | #
29 | # post "posts" do
30 | # halt 201, "The post was created successfully"
31 | # end
32 | # end
33 | #
34 | # @param endpoint [String] a url of the API service endpoint to mock.
35 | # Should only include hostname and schema.
36 | #
37 | # @yield a block to define behavior using Sinatra API
38 | #
39 | # @return [Mock5::Api] a mountable API object
40 | def mock(endpoint=nil, &block)
41 | Api.new(endpoint, &block)
42 | end
43 |
44 | # Mounts given list of APIs. Returns a list of APIs that were actually
45 | # mounted. The APIs that were already mounted when the method is called
46 | # are not included in the return value.
47 | #
48 | # @param apis [Enum #to_set] a list of APIs to mount
49 | #
50 | # @return [Set] a list of APIs actually mounted
51 | def mount(*apis)
52 | apis.to_set.subtract(mounted_apis).each do |api|
53 | check_api api
54 | mounted_apis.add api
55 | registry.register_request_stub api.request_stub
56 | end
57 | end
58 |
59 | # Unmount given APIs. Returns only the list of APIs that were actually
60 | # unmounted. If the API wasn't mounted when the method is called, it won't be
61 | # included in the return value.
62 | #
63 | # @param apis [Enum #to_set] a list of APIs to unmount
64 | #
65 | # @return [Set] a list of APIs actually unmounted
66 | def unmount(*apis)
67 | mounted_apis.intersection(apis).each do |api|
68 | mounted_apis.delete api
69 | if registry.request_stubs.include?(api.request_stub)
70 | registry.remove_request_stub api.request_stub
71 | end
72 | end
73 | end
74 |
75 | # Returns true if all given APIs are mounted. false otherwise.
76 | #
77 | # @param apis [Enum #to_set] a list of APIs to check
78 | #
79 | # @return [Boolean] true if all given APIs are mounted, false otherwise
80 | def mounted?(*apis)
81 | apis.to_set.subset?(mounted_apis)
82 | end
83 |
84 | # Mounts a list of given APIs, executes block and then unmounts them back.
85 | # Useful for wrapping around RSpec tests. It only unmounts APIs that were
86 | # not mounted before. Any API that was mounted before the method was
87 | # called remains mounted.
88 | #
89 | # @example
90 | # my_api = Mock5.mock("http://example.com") do
91 | # get "index.html" do
92 | # "
Hello world!
"
93 | # end
94 | # end
95 | #
96 | # another_api = Mock5.mock("http://foobar.com") do
97 | # get "hello/:what" do
98 | # "Hello #{params["what"]}
"
99 | # end
100 | # end
101 | #
102 | # Mock5.with_mounted my_api, another_api do
103 | # Net::HTTP.get("example.com", "/index.html") # => "Hello world!
"
104 | # Net::HTTP.get("foobar.com", "/hello/bar") # => "Hello, bar
"
105 | # end
106 | #
107 | # @param apis [Enum #to_set] a list of APIs to mount before executing the
108 | # block
109 | #
110 | # @yield the block to execute with given APIs being mounted
111 | def with_mounted(*apis)
112 | mounted = mount(*apis)
113 | yield
114 | ensure
115 | unmount *mounted
116 | end
117 |
118 | # Unmounts all currently mounted APIs and returns them
119 | #
120 | # @return [Set] a list of unmounted APIs
121 | def unmount_all!
122 | unmount *mounted_apis
123 | end
124 |
125 | alias_method :reset!, :unmount_all!
126 |
127 | private
128 |
129 | def registry
130 | WebMock::StubRegistry.instance
131 | end
132 |
133 | def check_api(api)
134 | fail ArgumentError, "expected an instance of Mock5::Api" unless Api === api
135 | end
136 | end
137 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # Mock5
2 | [](https://rubygems.org/gems/mock5)
3 | [](http://travis-ci.org/rwz/mock5)
4 | [](https://codeclimate.com/github/rwz/mock5)
5 | [](http://inch-ci.org/github/rwz/mock5)
6 |
7 | Mock5 allows to mock external APIs with simple Sinatra Rack apps.
8 |
9 | ## Installation
10 |
11 | This gem could be useful for testing, and maybe development purposes.
12 | Add it to the relevant groups in your Gemfile.
13 |
14 | ```ruby
15 | gem "mock5", groups: [:test, :development]
16 | ```
17 |
18 | and run `bundle`.
19 |
20 | ## Usage
21 |
22 | ### mock
23 | Use this method to describe API you're trying to mock.
24 |
25 | ```ruby
26 | weather_api = Mock5.mock("http://weather-api.com") do
27 | get "/weather.json" do
28 | MultiJson.dump(
29 | location: "Philadelphia, PA",
30 | temperature: "60F",
31 | description: "Sunny"
32 | )
33 | end
34 | end
35 | ```
36 |
37 | ### mount
38 | Use this method to enable API mocks you've defined previously.
39 |
40 | ```ruby
41 | Mock5.mount weather_api, some_other_api
42 | Net::HTTP.get("weather-api.com", "/weather.json") # => "{\"location\":...
43 | ```
44 |
45 | ### unmount
46 | Unmounts passed APIs if thery were previously mounted
47 |
48 | ```ruby
49 | Mock5.unmount some_other_api # [, and_another_api... ]
50 | ```
51 |
52 | ### mounted_apis
53 | This method returns a Set of all currently mounted APIs
54 |
55 | ```ruby
56 | Mock5.mounted_apis # => { weather_api }
57 | Mock5.mount another_api
58 | Mock5.mounted_apis # => { weather_api, another_api }
59 | ```
60 |
61 | ### with_mounted
62 | Executes the block with all given APIs mounted, and then unmounts them.
63 |
64 | ```ruby
65 | Mock5.mounted_apis # => { other_api }
66 | Mock5.with_mounted weather_api, other_api do
67 | Mock5.mounted_apis # => { other_api, weather_api }
68 | run_weather_api_test_suite!
69 | end
70 | Mock5.mounted_apis # => { other_api }
71 | ```
72 |
73 | ## Example
74 |
75 | Say you're writing a nice wrapper around remote user management REST API.
76 | You want your library to handle any unexpected situation aproppriately and
77 | show a relevant error message, or schedule a retry some time later.
78 |
79 | Obviously, you can't rely on a production API to test all these codepaths. You
80 | probably want a way to emulate all these situations locally. Enter Mock5:
81 |
82 | ```ruby
83 | # user registers successfully
84 | SuccessfulRegistration = Mock5.mock("http://example.com") do
85 | post "/users" do
86 | MultiJson.dump(
87 | first_name: "Zapp",
88 | last_name: "Brannigan",
89 | email: "zapp@planetexpress.com"
90 | )
91 | end
92 | end
93 |
94 | # registration returns validation error
95 | UnsuccessfulRegistration = Mock5.mock("http://example.com") do
96 | post "/users" do
97 | halt 406, MultiJson.dump(
98 | first_name: ["is too lame"],
99 | email: ["is not unique"]
100 | )
101 | end
102 | end
103 |
104 | # remote api is down for some reason
105 | RegistrationUnavailable = Mock5.mock("http://example.com") do
106 | post "/users" do
107 | halt 503, "Service Unavailable"
108 | end
109 | end
110 |
111 | # remote api times takes long time to respond
112 | RegistrationTimeout = Mock5.mock("http://example.com") do
113 | post "/users" do
114 | sleep 15
115 | end
116 | end
117 |
118 | describe MyApiWrapper do
119 | describe "successfull" do
120 | around do |example|
121 | Mock5.with_mounted(SuccessfulRegistration, &example)
122 | end
123 |
124 | it "allows user registration" do
125 | expect{ MyApiWrapper.register_user }.not_to raise_error
126 | end
127 | end
128 |
129 | describe "validation errors" do
130 | around do |example|
131 | Mock5.with_mounted(UnsuccessfulRegistration, &example)
132 | end
133 |
134 | it "raises a valiation error" do
135 | expect{ MyApiWrapper.register_user }.to raise_error(MyApiWrapper::ValidationError)
136 | end
137 | end
138 |
139 | describe "service is unavailable" do
140 | around do |example|
141 | Mock5.with_mounted(RegistrationUnavailable, &example)
142 | end
143 |
144 | it "raises a ServiceUnavailable error" do
145 | expect{ MyApiWrapper.register_user }.to raise_error(MyApiWrapper::ServiceUnavailable)
146 | end
147 | end
148 |
149 | describe "timeout" do
150 | around do |example|
151 | Mock5.with_mounted(RegistrationTimeout, &example)
152 | end
153 |
154 | it "raises timeout error" do
155 | expect{ MyApiWrapper.register_user }.to raise_error(Timeout::Error)
156 | end
157 | end
158 | end
159 | ```
160 |
161 | ## Contributing
162 |
163 | 1. Fork it
164 | 2. Create your feature branch (`git checkout -b my-new-feature`)
165 | 3. Commit your changes (`git commit -am 'Add some feature'`)
166 | 4. Push to the branch (`git push origin my-new-feature`)
167 | 5. Create new Pull Request
168 |
--------------------------------------------------------------------------------
/spec/mock5_spec.rb:
--------------------------------------------------------------------------------
1 | require "mock5"
2 |
3 | describe Mock5 do
4 | describe ".mock" do
5 | it "creates an Api" do
6 | expect(described_class::Api).to receive(:new).with(/foo/).and_yield
7 | described_class.mock /foo/ do
8 | # mock definition goes here
9 | end
10 | end
11 |
12 | it "returns an Api" do
13 | expect(described_class.mock).to be_kind_of(described_class::Api)
14 | end
15 | end
16 |
17 | describe "API mgmt" do
18 | before do
19 | described_class.instance_exec do
20 | if instance_variable_defined?(:@_mounted_apis)
21 | remove_instance_variable :@_mounted_apis
22 | end
23 | end
24 | end
25 |
26 | let(:mounted_apis){ described_class.mounted_apis }
27 | let(:mounted_apis_qty){ mounted_apis.size }
28 | let(:api){ described_class.mock }
29 | let(:another_api){ described_class.mock }
30 |
31 | describe ".mount" do
32 | it "raises ArgumentError when passed an invalid argument" do
33 | action = ->{ described_class.mount nil }
34 | message = "expected an instance of Mock5::Api"
35 | expect(&action).to raise_error(ArgumentError, message)
36 | end
37 | it "mounts an api" do
38 | described_class.mount api
39 | expect(mounted_apis).to include(api)
40 | end
41 |
42 | it "mounts an api only once" do
43 | 10.times{ described_class.mount api }
44 | expect(mounted_apis_qty).to eq(1)
45 | end
46 |
47 | it "mounts several APIs at once" do
48 | described_class.mount api, another_api
49 | expect(mounted_apis).to include(api)
50 | expect(mounted_apis).to include(another_api)
51 | end
52 |
53 | it "returns the list of mounted apis" do
54 | expect(described_class.mount(api)).to eq([api].to_set)
55 | expect(described_class.mount(api, another_api)).to eq([another_api].to_set)
56 | end
57 | end
58 |
59 | describe ".unmount" do
60 | before{ described_class.mount api }
61 |
62 | it "unmounts mounted api" do
63 | described_class.unmount api
64 | expect(mounted_apis).to be_empty
65 | end
66 |
67 | it "unmounts api only once" do
68 | 10.times{ described_class.unmount api }
69 | expect(mounted_apis).to be_empty
70 | end
71 |
72 | it "unmounts several APIs at once" do
73 | described_class.mount another_api
74 | expect(mounted_apis_qty).to eq(2)
75 | described_class.unmount api, another_api
76 | expect(mounted_apis).to be_empty
77 | end
78 |
79 | it "only unmount specified api" do
80 | described_class.mount another_api
81 | described_class.unmount api
82 | expect(mounted_apis).to include(another_api)
83 | end
84 |
85 | it "returns the list of unmounted apis" do
86 | expect(described_class.unmount(another_api)).to be_empty
87 | expect(described_class.unmount(api, another_api)).to eq([api].to_set)
88 | end
89 | end
90 |
91 | describe ".unmount_all!" do
92 | before do
93 | 3.times{ described_class.mount described_class.mock }
94 | end
95 |
96 | it "unmounts all currently mounted apis" do
97 | expect(mounted_apis_qty).to eq(3)
98 | described_class.unmount_all!
99 | expect(mounted_apis).to be_empty
100 | end
101 |
102 | it "has .reset! alias" do
103 | expect(mounted_apis_qty).to eq(3)
104 | described_class.reset!
105 | expect(mounted_apis).to be_empty
106 | end
107 | end
108 |
109 | describe ".mounted?" do
110 | before{ described_class.mount api }
111 |
112 | it "returns true if api is currently mounted" do
113 | expect(described_class.mounted?(api)).to be_truthy
114 | end
115 |
116 | it "returns false if api is currently not mounted" do
117 | expect(described_class.mounted?(another_api)).to be_falsy
118 | end
119 |
120 | it "returns true only when ALL api are mounted" do
121 | action = ->{ described_class.mount another_api }
122 | result = ->{ described_class.mounted? api, another_api }
123 | expect(&action).to change(&result).from(false).to(true)
124 | end
125 | end
126 |
127 | describe ".with_mounted" do
128 | it "temporary mounts an API" do
129 | action = -> do
130 | described_class.with_mounted api do
131 | expect(mounted_apis).to include(api)
132 | end
133 | end
134 |
135 | expect(mounted_apis).to be_empty
136 | expect(&action).not_to change(mounted_apis, :empty?)
137 | end
138 |
139 | it "doesn't unmount api, that was mounted before" do
140 | described_class.mount api
141 |
142 | described_class.with_mounted api, another_api do
143 | expect(mounted_apis).to include(another_api)
144 | end
145 |
146 | expect(mounted_apis).to include(api)
147 | expect(mounted_apis).not_to include(another_api)
148 | end
149 | end
150 |
151 | describe "stubbing" do
152 | def get(url)
153 | Net::HTTP.get(URI(url))
154 | end
155 |
156 | def post(url, params={})
157 | Net::HTTP.post_form(URI(url), params).body
158 | end
159 |
160 | let(:api) do
161 | described_class.mock "http://example.com" do
162 | get "/index.html" do
163 | "index.html"
164 | end
165 |
166 | post "/submit/here" do
167 | "submit"
168 | end
169 | end
170 | end
171 |
172 | let(:another_api) do
173 | described_class.mock "http://example.com" do
174 | post "/foo/:foo" do
175 | params["foo"]
176 | end
177 |
178 | get "/bar/:bar" do
179 | params["bar"]
180 | end
181 | end
182 | end
183 |
184 | context "#mount" do
185 | before{ described_class.mount api, another_api }
186 |
187 | it "stubs remote apis" do
188 | expect(get("http://example.com/index.html?foo=bar")).to eq("index.html")
189 | expect(post("http://example.com/submit/here?foo=bar")).to eq("submit")
190 | expect(post("http://example.com/foo/bar?fizz=buzz")).to eq("bar")
191 | expect(get("http://example.com/bar/foo")).to eq("foo")
192 | end
193 | end
194 |
195 | context "#with_mounted" do
196 | around do |example|
197 | described_class.with_mounted api, another_api, &example
198 | end
199 |
200 | it "stubs remote apis" do
201 | expect(get("http://example.com/index.html?foo=bar")).to eq("index.html")
202 | expect(post("http://example.com/submit/here?foo=bar")).to eq("submit")
203 | expect(post("http://example.com/foo/bar?fizz=buzz")).to eq("bar")
204 | expect(get("http://example.com/bar/foo")).to eq("foo")
205 | end
206 | end
207 | end
208 | end
209 | end
210 |
--------------------------------------------------------------------------------