faraday-hot_mock 0.4.1 → 0.6.0
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 +57 -2
- data/lib/faraday/hot_mock/adapter.rb +39 -3
- data/lib/faraday/hot_mock/version.rb +1 -1
- data/lib/faraday/hot_mock.rb +32 -9
- metadata +1 -1
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: 25e4f2498095f7292d5e11855332f571948d41f93e16afb2b7d3e3f29bebd243
|
|
4
|
+
data.tar.gz: d0a18131c29b31575ae29064f995c0eca098305ea776768fec305ef758122009
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: be6477e0289f117c0bbdd6e0b8d90d0b3bbe6897c51d61569d34b4bca8dffce90b34fce352e5aa9b2002acb2190373ce20ada2032ea629aceb9da4dedf7c3297
|
|
7
|
+
data.tar.gz: d6f897cc2f27bf7a4813968018acbbbcdedebb953790a19efa75544c6b51eaf85f1a3289cbe546f2d7443c5407a079a16727915589d890f737428ffa1d82aa63
|
data/README.md
CHANGED
|
@@ -81,14 +81,69 @@ It can also be a terrible idea if your mocks are designed for your specific need
|
|
|
81
81
|
|
|
82
82
|
It's up to you and your team what makes sense.
|
|
83
83
|
|
|
84
|
-
In most cases, it makes sense to check in the mocks for
|
|
84
|
+
In most cases, it makes sense to not check in the mocks for any environment, so in most cases you should add to your `.gitignore`:
|
|
85
85
|
|
|
86
86
|
```
|
|
87
87
|
# Ignore Faraday HotMocks except in test env
|
|
88
88
|
lib/faraday/mocks/**
|
|
89
|
-
!lib/faraday/mocks/test
|
|
90
89
|
```
|
|
91
90
|
|
|
91
|
+
If you're using scenarios, however, it's probably useful to check in any environment directory that has scenarios since they're only activated when a scenario is directly selected.
|
|
92
|
+
|
|
93
|
+
### Scenarios
|
|
94
|
+
|
|
95
|
+
You can use directories to conditionally group mocks. For example, you might want a "success" scenario and a "failure" scenario.
|
|
96
|
+
|
|
97
|
+
To do that, simply create the `/scenarios/success` and `/scenarios/failure` subdirectories within `lib/faraday/mocks/#{Rails.env}/`, and place the appropriate mock files in each, probably with the same endpoints but with different responses.
|
|
98
|
+
|
|
99
|
+
Then call `Faraday::HotMock.scenario = :success` or `Faraday::HotMock.scenario = :failure` to switch between them.
|
|
100
|
+
|
|
101
|
+
When a scenario is active, only mocks in that scenario directory will be considered. If no matching mock is found in that scenario, then the real request will be made.
|
|
102
|
+
|
|
103
|
+
To use the mocks not in the `scenarios` directory again, simply set `Faraday::HotMock.scenario = nil`.
|
|
104
|
+
|
|
105
|
+
### VCR Mode
|
|
106
|
+
|
|
107
|
+
VCR mode basically calls `Faraday::HotMock.record` on all requests made until it's turned off. You can start it with:
|
|
108
|
+
|
|
109
|
+
```ruby
|
|
110
|
+
Faraday::HotMock.vcr = true
|
|
111
|
+
```
|
|
112
|
+
|
|
113
|
+
Just like using `Faraday::HotMock.record` individually, these mocks are recorded to the main default file (`Faraday::HotMock.hot_mock_file`)
|
|
114
|
+
|
|
115
|
+
You can also record directly into a scenario, which will create the scenario directory structure, switch the current scenario and begin recording responses. This will continue until you set `Faraday::HotMock.vcr` to `false` or another scenario name.
|
|
116
|
+
|
|
117
|
+
```ruby
|
|
118
|
+
Faraday::HotMock.vcr = :new_scenario
|
|
119
|
+
```
|
|
120
|
+
|
|
121
|
+
> NOTE: VCR Mode does not overwrite previously recorded mocks
|
|
122
|
+
|
|
123
|
+
### Testing
|
|
124
|
+
|
|
125
|
+
In tests, you can certainly use a mocking library of choice. In many cases, that might be easier. This is because Faraday::HotMock is built for quick iteration using runtime-loaded YAML files, which isn't needed in tests.
|
|
126
|
+
|
|
127
|
+
If instead you want to use Faraday::HotMock, you can create mocked responses by hand, or use `Faraday::HotMock.mock!` to define mocks in a very similar way to other mocking libraries (similar to `stub_request` in WebMock, for example).
|
|
128
|
+
|
|
129
|
+
The most basic setup would be:
|
|
130
|
+
|
|
131
|
+
- Call `Faraday::HotMock.enable!` in your test setup
|
|
132
|
+
- Call `Faraday::HotMock.disable!` in your test teardown
|
|
133
|
+
- In a given test/spec or in a method, call `Faraday::HotMock.mock!(method: [method], url_pattern: [url], status: [status code], headers: [headers hash], body: [body])` to define a mock for that test/spec.
|
|
134
|
+
- Remove the mock file in teardown by referencing `Faraday::HotMock.hot_mock_file` (via `File.delete` or `FileUtils.rm`) since you probably don't want them to persist between tests
|
|
135
|
+
|
|
136
|
+
If you use scenarios, then you can do a bit less:
|
|
137
|
+
|
|
138
|
+
- Call `Faraday::HotMock.enable!` in your test setup
|
|
139
|
+
- Call `Faraday::HotMock.disable!` in your test teardown
|
|
140
|
+
- Call `Faraday::HotMock.scenario = :your_scenario_name` in a given test/spec
|
|
141
|
+
- Call `Faraday::HotMock.scenario = nil` in your test teardown if using scenarios
|
|
142
|
+
|
|
143
|
+
Overall, this may be a bit more work but it has the advantage that you can use the same mocking mechanism in both development and testing, which can reduce surprises when moving code between the two.
|
|
144
|
+
|
|
145
|
+
Small bonus: if you use `Faraday::HotMock` everywhere, you can remove the dependency for whichever mocking library/libraries you were using before.
|
|
146
|
+
|
|
92
147
|
### Convenience Methods
|
|
93
148
|
|
|
94
149
|
You can enable or disable mocking programmatically with these methods:
|
|
@@ -18,16 +18,52 @@ module Faraday
|
|
|
18
18
|
def call(env)
|
|
19
19
|
super
|
|
20
20
|
|
|
21
|
-
if Rails.env.production?
|
|
21
|
+
if Rails.env.production? || Faraday::HotMock.disabled?
|
|
22
22
|
return @fallback_adapter.new(@app, @options).call(env)
|
|
23
23
|
end
|
|
24
24
|
|
|
25
|
-
if Faraday::HotMock.
|
|
26
|
-
|
|
25
|
+
if Faraday::HotMock.vcr && !Faraday::HotMock.mocked?(method: env.method, url: env.url)
|
|
26
|
+
case Faraday::HotMock.vcr
|
|
27
|
+
when Symbol, String
|
|
28
|
+
Faraday::HotMock.record(method: env.method, url: env.url, into_scenario: Faraday::HotMock.vcr)
|
|
29
|
+
else
|
|
30
|
+
Faraday::HotMock.record(method: env.method, url: env.url)
|
|
31
|
+
end
|
|
32
|
+
end
|
|
33
|
+
|
|
34
|
+
if (mock = Faraday::HotMock.mocked?(method: env.method, url: env.url))
|
|
35
|
+
interpolate(mock, env) if mock_interpolated?(mock)
|
|
36
|
+
|
|
37
|
+
mock_response!(env, mock)
|
|
27
38
|
else
|
|
28
39
|
@fallback_adapter.new(@app, @options).call(env)
|
|
29
40
|
end
|
|
30
41
|
end
|
|
42
|
+
|
|
43
|
+
private
|
|
44
|
+
|
|
45
|
+
def mock_interpolated?(mock)
|
|
46
|
+
mock.key?("interpolate") && mock["body"].is_a?(Hash)
|
|
47
|
+
end
|
|
48
|
+
|
|
49
|
+
def interpolate(mock, env)
|
|
50
|
+
request_hash = JSON.parse(env.request_body) rescue {}
|
|
51
|
+
|
|
52
|
+
interpolated_hash = mock["interpolate"].transform_values do |v|
|
|
53
|
+
v = request_hash[v]
|
|
54
|
+
end.compact
|
|
55
|
+
|
|
56
|
+
mock["body"].merge!(interpolated_hash || {})
|
|
57
|
+
end
|
|
58
|
+
|
|
59
|
+
def mock_response!(env, mock)
|
|
60
|
+
save_response(
|
|
61
|
+
env,
|
|
62
|
+
mock["status"] || 200,
|
|
63
|
+
mock["body"] || "",
|
|
64
|
+
(mock["headers"] || {}).merge(Faraday::HotMock::HEADERS[:mocked] => "true")
|
|
65
|
+
)
|
|
66
|
+
end
|
|
31
67
|
end
|
|
32
68
|
end
|
|
33
69
|
end
|
data/lib/faraday/hot_mock.rb
CHANGED
|
@@ -7,6 +7,14 @@ module Faraday
|
|
|
7
7
|
module HotMock
|
|
8
8
|
extend self
|
|
9
9
|
|
|
10
|
+
attr_accessor :scenario, :vcr
|
|
11
|
+
|
|
12
|
+
HEADERS = {
|
|
13
|
+
recorded: "x-hot-mock-recorded-at",
|
|
14
|
+
mocked: "x-hot-mocked"
|
|
15
|
+
}
|
|
16
|
+
FILE_NAME = "hot_mocks.yml"
|
|
17
|
+
|
|
10
18
|
def disable!
|
|
11
19
|
FileUtils.rm_f(hot_mocking_file)
|
|
12
20
|
end
|
|
@@ -31,7 +39,7 @@ module Faraday
|
|
|
31
39
|
end
|
|
32
40
|
end
|
|
33
41
|
|
|
34
|
-
def
|
|
42
|
+
def delete(method:, url:)
|
|
35
43
|
return unless File.exist?(hot_mock_file)
|
|
36
44
|
|
|
37
45
|
mocks = YAML.load_file(hot_mock_file) || []
|
|
@@ -64,22 +72,25 @@ module Faraday
|
|
|
64
72
|
mocks.find { |entry| entry["method"].to_s.upcase == method.to_s.upcase && Regexp.new(entry["url_pattern"]).match?(url.to_s) } || false
|
|
65
73
|
end
|
|
66
74
|
|
|
67
|
-
def record(method:, url:)
|
|
75
|
+
def record(method:, url:, into_scenario: nil)
|
|
76
|
+
self.scenario = into_scenario if into_scenario.present?
|
|
77
|
+
|
|
68
78
|
return false if mocked?(method:, url:)
|
|
69
79
|
|
|
70
80
|
faraday = Faraday.new
|
|
71
81
|
|
|
72
82
|
response = faraday.send(method.downcase.to_sym, url)
|
|
73
83
|
|
|
84
|
+
FileUtils.mkdir_p(hot_mock_dir)
|
|
74
85
|
FileUtils.touch(hot_mock_file)
|
|
75
86
|
|
|
76
87
|
hot_mocks = YAML.load_file(hot_mock_file) || []
|
|
77
88
|
|
|
78
89
|
hot_mocks << {
|
|
79
90
|
"method" => method.to_s.upcase,
|
|
80
|
-
"url_pattern" => url,
|
|
91
|
+
"url_pattern" => url.to_s,
|
|
81
92
|
"status" => response.status,
|
|
82
|
-
"headers" => response.headers.to_h.merge(
|
|
93
|
+
"headers" => response.headers.to_h.merge(HEADERS[:recorded] => Time.now.utc.iso8601),
|
|
83
94
|
"body" => response.body
|
|
84
95
|
}
|
|
85
96
|
|
|
@@ -101,9 +112,9 @@ module Faraday
|
|
|
101
112
|
|
|
102
113
|
hot_mocks << {
|
|
103
114
|
"method" => method.to_s.upcase,
|
|
104
|
-
"url_pattern" => url,
|
|
115
|
+
"url_pattern" => url.to_s,
|
|
105
116
|
"status" => response.status,
|
|
106
|
-
"headers" => response.headers.to_h.merge(
|
|
117
|
+
"headers" => response.headers.to_h.merge(HEADERS[:recorded] => Time.now.utc.iso8601, "x-hot-mock" => "true"),
|
|
107
118
|
"body" => response.body
|
|
108
119
|
}
|
|
109
120
|
|
|
@@ -113,7 +124,15 @@ module Faraday
|
|
|
113
124
|
end
|
|
114
125
|
|
|
115
126
|
def hot_mock_dir
|
|
116
|
-
|
|
127
|
+
if self.scenario
|
|
128
|
+
Rails.root.join "lib/faraday/mocks/#{Rails.env}/scenarios/#{self.scenario}"
|
|
129
|
+
else
|
|
130
|
+
Rails.root.join "lib/faraday/mocks/#{Rails.env}"
|
|
131
|
+
end
|
|
132
|
+
end
|
|
133
|
+
|
|
134
|
+
def scenario_dir
|
|
135
|
+
Rails.root.join "lib/faraday/mocks/#{Rails.env}/scenarios"
|
|
117
136
|
end
|
|
118
137
|
|
|
119
138
|
def hot_mocking_file
|
|
@@ -121,7 +140,7 @@ module Faraday
|
|
|
121
140
|
end
|
|
122
141
|
|
|
123
142
|
def hot_mock_file
|
|
124
|
-
Rails.root.join(hot_mock_dir,
|
|
143
|
+
Rails.root.join(hot_mock_dir, FILE_NAME)
|
|
125
144
|
end
|
|
126
145
|
|
|
127
146
|
def mocks
|
|
@@ -138,7 +157,11 @@ module Faraday
|
|
|
138
157
|
end
|
|
139
158
|
|
|
140
159
|
def all_hot_mock_files
|
|
141
|
-
|
|
160
|
+
if scenario.present?
|
|
161
|
+
Dir.glob(File.join(hot_mock_dir, "**", "*.{yml,yaml}"))
|
|
162
|
+
else
|
|
163
|
+
Dir.glob(File.join(hot_mock_dir, "**", "*.{yml,yaml}")).reject { |path| path.include?("/scenarios/") }
|
|
164
|
+
end
|
|
142
165
|
end
|
|
143
166
|
end
|
|
144
167
|
end
|