mock5 1.0.3 → 1.0.5

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: 76be219294f80c132aa6185e6968ada828583e65
4
- data.tar.gz: 57a45abc38b9aeda5e26ca5dba4b7bd574ce527f
3
+ metadata.gz: 5e3ff7dc93593a4f21dd450ed0874f436d1493c9
4
+ data.tar.gz: 1599500a3e285e92e7931b4e24ce5790d0f6bc25
5
5
  SHA512:
6
- metadata.gz: 9f03b77ad3a4b76c135eaf5a0b138099350861a8340d59b465d84e869bb07f9fd0925955b1aa98a4a76fdba5ee9937641237972980b5fd7f2d121b8257514b43
7
- data.tar.gz: 4b2548315b2f6f24e71a15f8c39f0c3052fe4ade33c26d8d92a80a5088c46f33b78b3e28ff99c320b168d5d554fc16e29a4a2a5cf6fcfe0194f17fbcfdf4577d
6
+ metadata.gz: 6e93dd71cc6c4b953de995bd642764b17298a0e24c16ea2ede62d7c56dc0f70c71ad3397d9c456c43384f74f5c0f4b5dff1c1268d7a67a85026360a3f55b0005
7
+ data.tar.gz: 961aa86904f8e5f6fd54007202f4f107f4301174e01a864c9d72d94e180d0c931042b068b9cd21012148daafc98972cef47bfc126a0306120ab480f97eb9b884
data/README.md CHANGED
@@ -1,33 +1,88 @@
1
1
  # Mock5
2
- [![Gem Version](https://img.shields.io/gem/v/mock5.svg)][gem]
3
- [![Build Status](https://img.shields.io/travis/rwz/mock5.svg)][travis]
4
- [![Code Climate](https://img.shields.io/codeclimate/github/rwz/mock5.svg)][codeclimate]
5
- [gem]: https://rubygems.org/gems/mock5
6
- [travis]: http://travis-ci.org/rwz/mock5
7
- [codeclimate]: https://codeclimate.com/github/rwz/mock5
2
+ [![Gem Version](https://img.shields.io/gem/v/mock5.svg)](https://rubygems.org/gems/mock5)
3
+ [![Build Status](https://img.shields.io/travis/rwz/mock5.svg)](http://travis-ci.org/rwz/mock5)
4
+ [![Code Climate](https://img.shields.io/codeclimate/github/rwz/mock5.svg)](https://codeclimate.com/github/rwz/mock5)
5
+ [![Inline docs](http://inch-pages.github.io/github/rwz/mock5.svg)](http://inch-pages.github.io/github/rwz/mock5)
8
6
 
9
- Mock5 allows to mock external APIs with simple Sinatra Rake apps.
7
+ Mock5 allows to mock external APIs with simple Sinatra Rack apps.
10
8
 
11
9
  ## Installation
12
10
 
13
- Add this line to your application's Gemfile:
11
+ This gem could be useful for testing, and maybe development purposes.
12
+ Add it to the relevant groups in your Gemfile.
14
13
 
15
- gem "mock5"
14
+ ```ruby
15
+ gem "mock5", groups: [:test, :development]
16
+ ```
16
17
 
17
- And then execute:
18
+ and run `bundle`.
18
19
 
19
- $ bundle
20
+ ## Usage
20
21
 
21
- Or install it yourself as:
22
+ ### mock
23
+ Use this method to describe API you're trying to mock.
22
24
 
23
- $ gem install mock5
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
+ ```
24
36
 
25
- ## Usage
37
+ ### mount
38
+ Use this method to enable API mocks you've defined previously.
26
39
 
27
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
28
84
  SuccessfulRegistration = Mock5.mock("http://example.com") do
29
85
  post "/users" do
30
- # emulate successful registration
31
86
  MultiJson.dump(
32
87
  first_name: "Zapp",
33
88
  last_name: "Brannigan",
@@ -36,6 +91,7 @@ SuccessfulRegistration = Mock5.mock("http://example.com") do
36
91
  end
37
92
  end
38
93
 
94
+ # registration returns validation error
39
95
  UnsuccessfulRegistration = Mock5.mock("http://example.com") do
40
96
  post "/users" do
41
97
  halt 406, MultiJson.dump(
@@ -45,28 +101,58 @@ UnsuccessfulRegistration = Mock5.mock("http://example.com") do
45
101
  end
46
102
  end
47
103
 
48
- describe MyApi do
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
49
119
  describe "successfull" do
50
120
  around do |example|
51
- Mock5.with_mounted SuccessfulRegistration do
52
- example.call
53
- end
121
+ Mock5.with_mounted(SuccessfulRegistration, &example)
54
122
  end
55
123
 
56
124
  it "allows user registration" do
57
- expect{ MyApi.register_user }.not_to raise_error
125
+ expect{ MyApiWrapper.register_user }.not_to raise_error
58
126
  end
59
127
  end
60
128
 
61
129
  describe "validation errors" do
62
130
  around do |example|
63
- Mock5.with_mounted UnsuccessfulRegistration do
64
- example.call
65
- end
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)
66
152
  end
67
153
 
68
- it "returns errors" do
69
- expect{ MyApi.register_user }.to raise_error(MyApi::ValidationError)
154
+ it "raises timeout error" do
155
+ expect{ MyApiWrapper.register_user }.to raise_error(Timeout::Error)
70
156
  end
71
157
  end
72
158
  end
@@ -2,35 +2,108 @@ require "mock5/version"
2
2
  require "mock5/api"
3
3
  require "set"
4
4
 
5
+ # The main module of the gem, exposing all API management methods.
6
+ # Can be included into class.
5
7
  module Mock5
6
8
  extend self
7
9
 
10
+ # Returns a set of currently mounted APIs
11
+ #
12
+ # @return [Set] a list of currently mounted APIs
8
13
  def mounted_apis
9
14
  @_mounted_apis ||= Set.new
10
15
  end
11
16
 
12
- def mock(*args, &block)
13
- Api.new(*args, &block)
17
+ # Generates a new API
18
+ #
19
+ # @example
20
+ # my_mock_api = Mock5.mock("http://example.com") do
21
+ # get "posts" do
22
+ # [
23
+ # {id: 1, body: "a posy body"},
24
+ # {id: 2, body: "another post body"}
25
+ # ].to_json
26
+ # end
27
+ #
28
+ # post "posts" do
29
+ # halt 201, "The post was created successfully"
30
+ # end
31
+ # end
32
+ #
33
+ # @param endpoint [String] a url of the API service endpoint to mock.
34
+ # Should only include hostname and schema.
35
+ #
36
+ # @yield a block to define behavior using Sinatra API
37
+ #
38
+ # @return [Mock5::Api] a mountable API object
39
+ def mock(endpoint=nil, &block)
40
+ Api.new(endpoint, &block)
14
41
  end
15
42
 
43
+ # Mounts given list of APIs. Returns a list of APIs that were actually
44
+ # mounted. The APIs that were already mounted when the method is called
45
+ # are not included in the return value.
46
+ #
47
+ # @param apis [Enum #to_set] a list of APIs to mount
48
+ #
49
+ # @return [Set] a list of APIs actually mounted
16
50
  def mount(*apis)
17
- (apis.to_set - mounted_apis).each do |api|
51
+ apis.to_set.subtract(mounted_apis).each do |api|
18
52
  mounted_apis.add api
19
53
  registry.register_request_stub api.request_stub
20
54
  end
21
55
  end
22
56
 
57
+ # Unmount given APIs. Returns only the list of APIs that were actually
58
+ # unmounted. If the API wasn't mounted when the method is called, it won't be
59
+ # included in the return value.
60
+ #
61
+ # @param apis [Enum #to_set] a list of APIs to unmount
62
+ #
63
+ # @return [Set] a list of APIs actually unmounted
23
64
  def unmount(*apis)
24
- (mounted_apis & apis).each do |api|
65
+ mounted_apis.intersection(apis).each do |api|
25
66
  mounted_apis.delete api
26
67
  registry.remove_request_stub api.request_stub
27
68
  end
28
69
  end
29
70
 
71
+ # Returns true if all given APIs are mounted. false otherwise.
72
+ #
73
+ # @param apis [Enum #to_set] a list of APIs to check
74
+ #
75
+ # @return [Boolean] true if all given APIs are mounted, false otherwise
30
76
  def mounted?(*apis)
31
77
  apis.to_set.subset?(mounted_apis)
32
78
  end
33
79
 
80
+ # Mounts a list of given APIs, executes block and then unmounts them back.
81
+ # Useful for wrapping around RSpec tests. It only unmounts APIs that were
82
+ # not mounted before. Any API that was mounted before the method was
83
+ # called remains mounted.
84
+ #
85
+ # @example
86
+ # my_api = Mock5.mock("http://example.com") do
87
+ # get "index.html" do
88
+ # "<h1>Hello world!</h1>"
89
+ # end
90
+ # end
91
+ #
92
+ # another_api = Mock5.mock("http://foobar.com") do
93
+ # get "hello/:what" do
94
+ # "<h1>Hello #{params["what"]}</h1>"
95
+ # end
96
+ # end
97
+ #
98
+ # Mock5.with_mounted my_api, another_api do
99
+ # Net::HTTP.get("example.com", "/index.html") # => "<h1>Hello world!</h1>"
100
+ # Net::HTTP.get("foobar.com", "/hello/bar") # => "<h1>Hello, bar</h1>"
101
+ # end
102
+ #
103
+ # @param apis [Enum #to_set] a list of APIs to mount before executing the
104
+ # block
105
+ #
106
+ # @yield the block to execute with given APIs being mounted
34
107
  def with_mounted(*apis)
35
108
  mounted = mount(*apis)
36
109
  yield
@@ -38,6 +111,9 @@ module Mock5
38
111
  unmount *mounted
39
112
  end
40
113
 
114
+ # Unmounts all currently mounted APIs and returns them
115
+ #
116
+ # @return [Set] a list of unmounted APIs
41
117
  def unmount_all!
42
118
  unmount *mounted_apis
43
119
  end
@@ -3,14 +3,46 @@ require "sinatra"
3
3
  require "webmock"
4
4
 
5
5
  module Mock5
6
+ # A class representing an API mock
6
7
  class Api
7
- attr_reader :endpoint, :app
8
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]
9
38
  def initialize(endpoint=nil, &block)
10
39
  @app = Sinatra.new(&block)
11
40
  @endpoint = normalize_endpoint(endpoint)
12
41
  end
13
42
 
43
+ # Returns webmock request stub built with Sinatra app and enpoint url
44
+ #
45
+ # @return [WebMock::RequestStub]
14
46
  def request_stub
15
47
  @request_stub ||= WebMock::RequestStub.new(:any, endpoint).tap{ |s| s.to_rack(app) }
16
48
  end
@@ -1,3 +1,3 @@
1
1
  module Mock5
2
- VERSION = "1.0.3".freeze
2
+ VERSION = "1.0.5".freeze
3
3
  end
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: mock5
3
3
  version: !ruby/object:Gem::Version
4
- version: 1.0.3
4
+ version: 1.0.5
5
5
  platform: ruby
6
6
  authors:
7
7
  - Pavel Pravosud
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2014-03-13 00:00:00.000000000 Z
11
+ date: 2014-03-29 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: webmock
@@ -84,3 +84,4 @@ summary: Mock APIs using Sinatra
84
84
  test_files:
85
85
  - spec/mock5_api_spec.rb
86
86
  - spec/mock5_spec.rb
87
+ has_rdoc: