fixturama 0.0.7 → 0.4.1
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/CHANGELOG.md +100 -0
- data/README.md +71 -3
- data/lib/fixturama.rb +49 -20
- data/lib/fixturama/changes.rb +72 -0
- data/lib/fixturama/changes/base.rb +28 -0
- data/lib/fixturama/changes/chain.rb +64 -0
- data/lib/fixturama/changes/chain/actions.rb +31 -0
- data/lib/fixturama/changes/chain/arguments.rb +59 -0
- data/lib/fixturama/changes/chain/raise_action.rb +43 -0
- data/lib/fixturama/changes/chain/return_action.rb +33 -0
- data/lib/fixturama/changes/const.rb +30 -0
- data/lib/fixturama/changes/env.rb +35 -0
- data/lib/fixturama/changes/request.rb +91 -0
- data/lib/fixturama/changes/request/response.rb +62 -0
- data/lib/fixturama/changes/request/responses.rb +22 -0
- data/lib/fixturama/changes/seed.rb +39 -0
- data/lib/fixturama/config.rb +5 -0
- data/lib/fixturama/fixture_error.rb +31 -0
- data/lib/fixturama/loader.rb +1 -0
- data/lib/fixturama/loader/context.rb +1 -0
- data/lib/fixturama/loader/value.rb +1 -0
- data/lib/fixturama/version.rb +4 -0
- metadata +80 -50
- data/.gitignore +0 -12
- data/.rspec +0 -2
- data/.rubocop.yml +0 -22
- data/.travis.yml +0 -23
- data/Gemfile +0 -9
- data/LICENSE.txt +0 -21
- data/Rakefile +0 -6
- data/fixturama.gemspec +0 -23
- data/lib/fixturama/seed.rb +0 -15
- data/lib/fixturama/stubs.rb +0 -57
- data/lib/fixturama/stubs/chain.rb +0 -89
- data/lib/fixturama/stubs/chain/actions.rb +0 -39
- data/lib/fixturama/stubs/chain/actions/raise.rb +0 -21
- data/lib/fixturama/stubs/chain/actions/return.rb +0 -23
- data/lib/fixturama/stubs/chain/arguments.rb +0 -86
- data/lib/fixturama/stubs/const.rb +0 -42
- data/lib/fixturama/utils.rb +0 -39
- data/spec/fixturama/load_fixture/_spec.rb +0 -53
- data/spec/fixturama/load_fixture/data.json +0 -5
- data/spec/fixturama/load_fixture/data.yaml +0 -3
- data/spec/fixturama/load_fixture/data.yml +0 -3
- data/spec/fixturama/seed_fixture/_spec.rb +0 -33
- data/spec/fixturama/seed_fixture/seed.yml +0 -16
- data/spec/fixturama/stub_fixture/_spec.rb +0 -96
- data/spec/fixturama/stub_fixture/stub.yml +0 -60
- data/spec/spec_helper.rb +0 -26
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 779a1dc8057604e6a5524eff0ef07520ecdbaf62895711411a312fb5c81a7558
|
4
|
+
data.tar.gz: 7976718e10f2fe8036d9e934d6e720cdcb91890f4263221720bfe76815eca071
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 25047655a38dedca47af366563ad14c7f0bea54ddd0230097f6a940b127593a70a6144cf556670308dc1ad1048cce74180cecfe7c89871682602e5e5bbca0988
|
7
|
+
data.tar.gz: c23c3fe8662badf370ffc5cb02c756d510ca2be2f75639bc77e8d54f4243b49be74f17f54561329861865778a06b7e08efcf1c24d6fbb873dfacbb912f53618b
|
data/CHANGELOG.md
CHANGED
@@ -5,6 +5,101 @@ All notable changes to this project will be documented in this file.
|
|
5
5
|
The format is based on [Keep a Changelog](http://keepachangelog.com/)
|
6
6
|
and this project adheres to [Semantic Versioning](http://semver.org/).
|
7
7
|
|
8
|
+
## [0.4.1] - [2021-03-31]
|
9
|
+
|
10
|
+
### Fixed
|
11
|
+
|
12
|
+
- Dependency from hashie updated to allow v4+ (nepalez)
|
13
|
+
|
14
|
+
## [0.4.0] - [2020-03-15]
|
15
|
+
|
16
|
+
### Added
|
17
|
+
|
18
|
+
- Support for stubbing ENV variables (nepalez)
|
19
|
+
|
20
|
+
```yaml
|
21
|
+
---
|
22
|
+
- env: GOOGLE_CLOUD_KEY
|
23
|
+
value: foo
|
24
|
+
|
25
|
+
- env: GOOGLE_CLOUD_PASSWORD
|
26
|
+
value: bar
|
27
|
+
```
|
28
|
+
|
29
|
+
This would stub selected variables only, not touching the others
|
30
|
+
|
31
|
+
## [0.3.0] - [2020-03-08]
|
32
|
+
|
33
|
+
### Added
|
34
|
+
|
35
|
+
- Support for exception arguments (nepalez)
|
36
|
+
|
37
|
+
```yaml
|
38
|
+
---
|
39
|
+
- class: API
|
40
|
+
chain: get_product
|
41
|
+
arguments:
|
42
|
+
- 1
|
43
|
+
actions:
|
44
|
+
- raise: API::NotFoundError
|
45
|
+
arguments: # <--- that's that
|
46
|
+
- "Cannot find a product by id: 1"
|
47
|
+
```
|
48
|
+
|
49
|
+
which would raise `API::NotFoundError.new("Cannot find a product by id: 1")`
|
50
|
+
|
51
|
+
## [0.2.0] - [2020-02-17]
|
52
|
+
|
53
|
+
### Added
|
54
|
+
|
55
|
+
- Stubbing and seeding from the same source file via the `call_fixture` method (nepalez)
|
56
|
+
|
57
|
+
```yaml
|
58
|
+
# ./changes.yml
|
59
|
+
---
|
60
|
+
- type: user
|
61
|
+
params:
|
62
|
+
id: 1
|
63
|
+
|
64
|
+
- const: DEFAULT_USER_ID
|
65
|
+
value: 1
|
66
|
+
|
67
|
+
- url: https://example.com/users/default
|
68
|
+
method: get
|
69
|
+
responses:
|
70
|
+
- body:
|
71
|
+
id: 1
|
72
|
+
name: Andrew
|
73
|
+
```
|
74
|
+
|
75
|
+
```ruby
|
76
|
+
before { call_fixture "#{__dir__}/changes.yml" }
|
77
|
+
```
|
78
|
+
|
79
|
+
## [0.1.0] - [2020-02-09]
|
80
|
+
|
81
|
+
### Added
|
82
|
+
|
83
|
+
- Stubbing of the HTTP requests using webmock (nepalez)
|
84
|
+
|
85
|
+
```yaml
|
86
|
+
---
|
87
|
+
- url: example.com/foo
|
88
|
+
method: get
|
89
|
+
body: foobar
|
90
|
+
query:
|
91
|
+
foo: bar
|
92
|
+
basic_auth:
|
93
|
+
user: foo
|
94
|
+
password: bar
|
95
|
+
headers:
|
96
|
+
Accept: utf-8
|
97
|
+
responses:
|
98
|
+
- status: 200
|
99
|
+
body: foobar
|
100
|
+
- status: 404
|
101
|
+
```
|
102
|
+
|
8
103
|
## [0.0.7] - [2019-07-01]
|
9
104
|
|
10
105
|
### Added
|
@@ -175,3 +270,8 @@ This is a first public release with features extracted from production app.
|
|
175
270
|
[0.0.5]: https://github.com/nepalez/fixturama/compare/v0.0.4...v0.0.5
|
176
271
|
[0.0.6]: https://github.com/nepalez/fixturama/compare/v0.0.5...v0.0.6
|
177
272
|
[0.0.7]: https://github.com/nepalez/fixturama/compare/v0.0.6...v0.0.7
|
273
|
+
[0.1.0]: https://github.com/nepalez/fixturama/compare/v0.0.7...v0.1.0
|
274
|
+
[0.2.0]: https://github.com/nepalez/fixturama/compare/v0.1.0...v0.2.0
|
275
|
+
[0.3.0]: https://github.com/nepalez/fixturama/compare/v0.2.0...v0.3.0
|
276
|
+
[0.4.0]: https://github.com/nepalez/fixturama/compare/v0.3.0...v0.4.0
|
277
|
+
[0.4.1]: https://github.com/nepalez/fixturama/compare/v0.4.0...v0.4.1
|
data/README.md
CHANGED
@@ -124,21 +124,41 @@ Use the `count: 2` key to create more objects at once.
|
|
124
124
|
|
125
125
|
### Stubbing
|
126
126
|
|
127
|
-
|
127
|
+
The gem supports stubbing message chains, constants and http requests with the following keys.
|
128
128
|
|
129
129
|
For message chains:
|
130
130
|
|
131
131
|
- `class` for stubbed class
|
132
132
|
- `chain` for messages chain
|
133
133
|
- `arguments` (optional) for specific arguments
|
134
|
-
- `actions` for an array of actions for consecutive invocations of the chain
|
134
|
+
- `actions` for an array of actions for consecutive invocations of the chain with keys
|
135
|
+
- `return` for a value to be returned
|
136
|
+
- `raise` for an exception to be risen
|
137
|
+
- `repeate` for a number of invocations with this action
|
135
138
|
|
136
139
|
For constants:
|
137
140
|
|
138
141
|
- `const` for stubbed constant
|
139
142
|
- `value` for a value of the constant
|
140
143
|
|
141
|
-
|
144
|
+
For environment variables:
|
145
|
+
|
146
|
+
- `env` for the name of a variable
|
147
|
+
`value` for a value of the variable
|
148
|
+
|
149
|
+
For http requests:
|
150
|
+
|
151
|
+
- `url` or `uri` for the URI of the request (treats values like `/.../` as regular expressions)
|
152
|
+
- `method` for the specific http-method (like `get` or `post`)
|
153
|
+
- `body` for the request body (treats values like `/.../` as regular expressions)
|
154
|
+
- `headers` for the request headers
|
155
|
+
- `query` for the request query
|
156
|
+
- `basic_auth` for the `user` and `password` of basic authentication
|
157
|
+
- `response` or `responses` for consecutively envoked responses with keys:
|
158
|
+
- `status`
|
159
|
+
- `body`
|
160
|
+
- `headers`
|
161
|
+
- `repeate` for the number of times this response should be returned before switching to the next one
|
142
162
|
|
143
163
|
```yaml
|
144
164
|
# ./stubs.yml
|
@@ -166,10 +186,34 @@ Every action either `return` some value, or `raise` some exception
|
|
166
186
|
- <%= profile_id %>
|
167
187
|
actions:
|
168
188
|
- return: true
|
189
|
+
repeate: 1 # this is the default value
|
169
190
|
- raise: ActiveRecord::RecordNotFound
|
191
|
+
arguments:
|
192
|
+
- "Profile with id: 1 not found" # for error message
|
170
193
|
|
194
|
+
# Here we stubbing a constant
|
171
195
|
- const: NOTIFIER_TIMEOUT_SEC
|
172
196
|
value: 10
|
197
|
+
|
198
|
+
# This is a stub for ENV['DEFAULT_EMAIL']
|
199
|
+
- env: DEFAULT_EMAIL
|
200
|
+
value: foo@example.com
|
201
|
+
|
202
|
+
# Examples for stubbing HTTP
|
203
|
+
- uri: /example.com/foo/ # regexp!
|
204
|
+
method: delete
|
205
|
+
basic_auth:
|
206
|
+
user: foo
|
207
|
+
password: bar
|
208
|
+
responses:
|
209
|
+
- status: 200 # for the first call
|
210
|
+
repeate: 1 # this is the default value, but you can set another one
|
211
|
+
- status: 404 # for any other call
|
212
|
+
|
213
|
+
- uri: htpps://example.com/foo # exact string!
|
214
|
+
method: delete
|
215
|
+
responses:
|
216
|
+
- status: 401
|
173
217
|
```
|
174
218
|
|
175
219
|
```graphql
|
@@ -203,6 +247,30 @@ I find it especially helpful when I need to check different edge cases. Instead
|
|
203
247
|
|
204
248
|
Looking at the spec I can easily figure out the "structure" of expectation, while looking at fixtures I can check the concrete corner cases.
|
205
249
|
|
250
|
+
## Single Source of Changes
|
251
|
+
|
252
|
+
If you will, you can list all stubs and seeds at the one single file like
|
253
|
+
|
254
|
+
```yaml
|
255
|
+
# ./changes.yml
|
256
|
+
---
|
257
|
+
- type: user
|
258
|
+
params:
|
259
|
+
id: 1
|
260
|
+
name: Andrew
|
261
|
+
|
262
|
+
- const: DEFAULT_USER_ID
|
263
|
+
value: 1
|
264
|
+
```
|
265
|
+
|
266
|
+
This fixture can be applied via `call_fixture` method just like we did above with `seed_fixture` and `stub_fixture`:
|
267
|
+
|
268
|
+
```ruby
|
269
|
+
before { call_fixture "#{__dir__}/changes.yml" }
|
270
|
+
```
|
271
|
+
|
272
|
+
In fact, since the `v0.2.0` all those methods are just the aliases of the `call_fixture`.
|
273
|
+
|
206
274
|
## License
|
207
275
|
|
208
276
|
The gem is available as open source under the terms of the [MIT License][license].
|
data/lib/fixturama.rb
CHANGED
@@ -3,46 +3,75 @@ require "factory_bot"
|
|
3
3
|
require "hashie/mash"
|
4
4
|
require "json"
|
5
5
|
require "rspec"
|
6
|
+
require "webmock/rspec"
|
6
7
|
require "yaml"
|
7
8
|
|
9
|
+
#
|
10
|
+
# A set of helpers to prettify specs with fixtures
|
11
|
+
#
|
8
12
|
module Fixturama
|
13
|
+
require_relative "fixturama/fixture_error"
|
9
14
|
require_relative "fixturama/config"
|
10
|
-
require_relative "fixturama/utils"
|
11
15
|
require_relative "fixturama/loader"
|
12
|
-
require_relative "fixturama/
|
13
|
-
require_relative "fixturama/seed"
|
16
|
+
require_relative "fixturama/changes"
|
14
17
|
|
18
|
+
# Set the initial value for database-generated IDs
|
19
|
+
# @param [#to_i] value
|
20
|
+
# @return [Fixturama]
|
15
21
|
def self.start_ids_from(value)
|
16
22
|
Config.start_ids_from(value)
|
23
|
+
self
|
17
24
|
end
|
18
25
|
|
19
|
-
|
20
|
-
|
21
|
-
|
26
|
+
# @!method read_fixture(path, options)
|
27
|
+
# Read the text content of the fixture
|
28
|
+
# @param [#to_s] path The path to the fixture file
|
29
|
+
# @param [Hash<Symbol, _>] options
|
30
|
+
# The list of options to be accessible in the fixture
|
31
|
+
# @return [String]
|
32
|
+
def read_fixture(path, **options)
|
33
|
+
content = File.read(path)
|
34
|
+
hashie = Hashie::Mash.new(options)
|
35
|
+
bindings = hashie.instance_eval { binding }
|
22
36
|
|
23
|
-
|
24
|
-
fixturama_stubs.apply(self)
|
37
|
+
ERB.new(content).result(bindings)
|
25
38
|
end
|
26
39
|
|
27
|
-
|
28
|
-
|
40
|
+
# @!method load_fixture(path, options)
|
41
|
+
# Load data from a fixture
|
42
|
+
# @param (see #read_fixture)
|
43
|
+
# @return [Object]
|
44
|
+
def load_fixture(path, **options)
|
45
|
+
Loader.new(path, options).call
|
29
46
|
end
|
30
47
|
|
31
|
-
|
32
|
-
|
48
|
+
# @!method call_fixture(path, options)
|
49
|
+
# Stub different objects and seed the database from a fixture
|
50
|
+
# @param (see #read_fixture)
|
51
|
+
# @return [RSpec::Core::Example] the current example
|
52
|
+
def call_fixture(path, **options)
|
53
|
+
items = Array load_fixture(path, **options)
|
54
|
+
items.each { |item| changes.add(item) }
|
55
|
+
tap { changes.call(self) }
|
56
|
+
rescue FixtureError => err
|
57
|
+
raise err.with_file(path)
|
33
58
|
end
|
34
59
|
|
35
|
-
|
36
|
-
|
37
|
-
|
38
|
-
|
60
|
+
# @!method seed_fixture(path, options)
|
61
|
+
# The alias for the +call_fixture+
|
62
|
+
# @param (see #call_fixture)
|
63
|
+
# @return (see #call_fixture)
|
64
|
+
alias seed_fixture call_fixture
|
39
65
|
|
40
|
-
|
41
|
-
|
66
|
+
# @!method stub_fixture(path, options)
|
67
|
+
# The alias for the +call_fixture+
|
68
|
+
# @param (see #call_fixture)
|
69
|
+
# @return (see #call_fixture)
|
70
|
+
alias stub_fixture call_fixture
|
42
71
|
|
43
72
|
private
|
44
73
|
|
45
|
-
def
|
46
|
-
@
|
74
|
+
def changes
|
75
|
+
@changes ||= Changes.new
|
47
76
|
end
|
48
77
|
end
|
@@ -0,0 +1,72 @@
|
|
1
|
+
module Fixturama
|
2
|
+
#
|
3
|
+
# @private
|
4
|
+
# Registry of changes (stubs and seeds)
|
5
|
+
#
|
6
|
+
class Changes
|
7
|
+
require_relative "changes/base"
|
8
|
+
require_relative "changes/chain"
|
9
|
+
require_relative "changes/const"
|
10
|
+
require_relative "changes/env"
|
11
|
+
require_relative "changes/request"
|
12
|
+
require_relative "changes/seed"
|
13
|
+
|
14
|
+
# Match option keys to the type of an item
|
15
|
+
TYPES = {
|
16
|
+
actions: Chain,
|
17
|
+
arguments: Chain,
|
18
|
+
basic_auth: Request,
|
19
|
+
body: Request,
|
20
|
+
chain: Chain,
|
21
|
+
class: Chain,
|
22
|
+
const: Const,
|
23
|
+
count: Seed,
|
24
|
+
env: Env,
|
25
|
+
headers: Request,
|
26
|
+
http_method: Request,
|
27
|
+
object: Chain,
|
28
|
+
params: Seed,
|
29
|
+
query: Request,
|
30
|
+
response: Request,
|
31
|
+
responses: Request,
|
32
|
+
traits: Seed,
|
33
|
+
type: Seed,
|
34
|
+
uri: Request,
|
35
|
+
url: Request,
|
36
|
+
}.freeze
|
37
|
+
|
38
|
+
# Adds new change to the registry
|
39
|
+
# @param [Hash] options
|
40
|
+
# @return [Fixturama::Changes]
|
41
|
+
# @raise [Fixturama::FixtureError] if the options cannot be processed
|
42
|
+
def add(options)
|
43
|
+
options = Hash(options).transform_keys(&:to_sym)
|
44
|
+
types = options.keys.map { |key| TYPES[key] }.compact.uniq
|
45
|
+
raise "Wrong count" unless types.count == 1
|
46
|
+
|
47
|
+
@changes << types.first.new(options)
|
48
|
+
self
|
49
|
+
rescue FixtureError => err
|
50
|
+
raise err
|
51
|
+
rescue StandardError => err
|
52
|
+
raise FixtureError.new("an operation", options, err)
|
53
|
+
end
|
54
|
+
|
55
|
+
# Apply all registered changes to the RSpec example
|
56
|
+
# @param [RSpec::Core::Example] example
|
57
|
+
# @return [self]
|
58
|
+
def call(example)
|
59
|
+
@changes
|
60
|
+
.group_by(&:key)
|
61
|
+
.values
|
62
|
+
.map { |changes| changes.reduce :merge }
|
63
|
+
.each { |change| change.call(example) }
|
64
|
+
end
|
65
|
+
|
66
|
+
private
|
67
|
+
|
68
|
+
def initialize
|
69
|
+
@changes = []
|
70
|
+
end
|
71
|
+
end
|
72
|
+
end
|
@@ -0,0 +1,28 @@
|
|
1
|
+
class Fixturama::Changes
|
2
|
+
#
|
3
|
+
# @private
|
4
|
+
# @abstract
|
5
|
+
# Base class for changes downloaded from a fixture
|
6
|
+
#
|
7
|
+
class Base
|
8
|
+
# @!attribute [r] key The key identifier of the change
|
9
|
+
# @return [String]
|
10
|
+
alias key hash
|
11
|
+
|
12
|
+
# Merge the other change into the current one
|
13
|
+
# @param [Fixturama::Changes::Base] other
|
14
|
+
# @return [Fixturama::Changes::Base]
|
15
|
+
def merge(other)
|
16
|
+
# By default just take the other change if applicable
|
17
|
+
other.class == self.class && other.key == key ? other : self
|
18
|
+
end
|
19
|
+
|
20
|
+
# @abstract
|
21
|
+
# Call the corresponding change (either a stub or a seed)
|
22
|
+
# @param [RSpec::Core::Example] _example The RSpec example
|
23
|
+
# @return [self]
|
24
|
+
def call(_example)
|
25
|
+
self
|
26
|
+
end
|
27
|
+
end
|
28
|
+
end
|
@@ -0,0 +1,64 @@
|
|
1
|
+
class Fixturama::Changes
|
2
|
+
#
|
3
|
+
# @private
|
4
|
+
# Stub a chain of messages
|
5
|
+
#
|
6
|
+
class Chain < Base
|
7
|
+
require_relative "chain/raise_action"
|
8
|
+
require_relative "chain/return_action"
|
9
|
+
require_relative "chain/actions"
|
10
|
+
require_relative "chain/arguments"
|
11
|
+
|
12
|
+
def key
|
13
|
+
@key ||= ["chain", @receiver.name, *@messages].join(".")
|
14
|
+
end
|
15
|
+
|
16
|
+
def merge(other)
|
17
|
+
return self unless other.class == self.class && other.key == key
|
18
|
+
|
19
|
+
tap { @arguments = (other.arguments | arguments).sort_by(&:order) }
|
20
|
+
end
|
21
|
+
|
22
|
+
def call(example)
|
23
|
+
call_action = example.send(:receive_message_chain, *@messages) do |*real|
|
24
|
+
action = arguments.find { |expected| expected.match?(*real) }
|
25
|
+
action ? action.call : raise("Unexpected arguments: #{real}")
|
26
|
+
end
|
27
|
+
|
28
|
+
example.send(:allow, @receiver).to call_action
|
29
|
+
end
|
30
|
+
|
31
|
+
protected
|
32
|
+
|
33
|
+
attr_reader :arguments
|
34
|
+
|
35
|
+
private
|
36
|
+
|
37
|
+
def initialize(**options)
|
38
|
+
@receiver = receiver_from(options)
|
39
|
+
@messages = messages_from(options)
|
40
|
+
@arguments = [Arguments.new(options)]
|
41
|
+
end
|
42
|
+
|
43
|
+
def receiver_from(options)
|
44
|
+
case options.slice(:class, :object).keys
|
45
|
+
when %i[class] then Kernel.const_get(options[:class])
|
46
|
+
when %i[object] then Object.send(:eval, options[:object])
|
47
|
+
else raise
|
48
|
+
end
|
49
|
+
rescue StandardError => err
|
50
|
+
raise Fixturama::FixtureError.new("a stabbed object", options, err)
|
51
|
+
end
|
52
|
+
|
53
|
+
def messages_from(options)
|
54
|
+
case value = options[:chain]
|
55
|
+
when Array then value.map(&:to_sym)
|
56
|
+
when String then [value.to_sym]
|
57
|
+
when Symbol then value
|
58
|
+
else raise
|
59
|
+
end
|
60
|
+
rescue StandardError => err
|
61
|
+
raise Fixturama::FixtureError.new("a messages chain", options, err)
|
62
|
+
end
|
63
|
+
end
|
64
|
+
end
|