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 +4 -4
- data/README.md +111 -25
- data/lib/mock5.rb +80 -4
- data/lib/mock5/api.rb +33 -1
- data/lib/mock5/version.rb +1 -1
- metadata +3 -2
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 5e3ff7dc93593a4f21dd450ed0874f436d1493c9
|
4
|
+
data.tar.gz: 1599500a3e285e92e7931b4e24ce5790d0f6bc25
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 6e93dd71cc6c4b953de995bd642764b17298a0e24c16ea2ede62d7c56dc0f70c71ad3397d9c456c43384f74f5c0f4b5dff1c1268d7a67a85026360a3f55b0005
|
7
|
+
data.tar.gz: 961aa86904f8e5f6fd54007202f4f107f4301174e01a864c9d72d94e180d0c931042b068b9cd21012148daafc98972cef47bfc126a0306120ab480f97eb9b884
|
data/README.md
CHANGED
@@ -1,33 +1,88 @@
|
|
1
1
|
# Mock5
|
2
|
-
[]
|
3
|
-
[]
|
4
|
-
[]
|
5
|
-
[
|
6
|
-
[travis]: http://travis-ci.org/rwz/mock5
|
7
|
-
[codeclimate]: https://codeclimate.com/github/rwz/mock5
|
2
|
+
[](https://rubygems.org/gems/mock5)
|
3
|
+
[](http://travis-ci.org/rwz/mock5)
|
4
|
+
[](https://codeclimate.com/github/rwz/mock5)
|
5
|
+
[](http://inch-pages.github.io/github/rwz/mock5)
|
8
6
|
|
9
|
-
Mock5 allows to mock external APIs with simple Sinatra
|
7
|
+
Mock5 allows to mock external APIs with simple Sinatra Rack apps.
|
10
8
|
|
11
9
|
## Installation
|
12
10
|
|
13
|
-
|
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
|
-
|
14
|
+
```ruby
|
15
|
+
gem "mock5", groups: [:test, :development]
|
16
|
+
```
|
16
17
|
|
17
|
-
|
18
|
+
and run `bundle`.
|
18
19
|
|
19
|
-
|
20
|
+
## Usage
|
20
21
|
|
21
|
-
|
22
|
+
### mock
|
23
|
+
Use this method to describe API you're trying to mock.
|
22
24
|
|
23
|
-
|
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
|
-
|
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
|
-
|
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
|
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{
|
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
|
64
|
-
|
65
|
-
|
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 "
|
69
|
-
expect{
|
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
|
data/lib/mock5.rb
CHANGED
@@ -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
|
-
|
13
|
-
|
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
|
-
|
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
|
-
(
|
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
|
data/lib/mock5/api.rb
CHANGED
@@ -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
|
data/lib/mock5/version.rb
CHANGED
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.
|
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-
|
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:
|