mock5 1.0.3 → 1.0.5
Sign up to get free protection for your applications and to get access to all the features.
- 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
|
-
[![Gem Version](https://img.shields.io/gem/v/mock5.svg)]
|
3
|
-
[![Build Status](https://img.shields.io/travis/rwz/mock5.svg)]
|
4
|
-
[![Code Climate](https://img.shields.io/codeclimate/github/rwz/mock5.svg)]
|
5
|
-
[
|
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
|
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:
|