fixturama 0.0.7 → 0.4.1

Sign up to get free protection for your applications and to get access to all the features.
Files changed (50) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +100 -0
  3. data/README.md +71 -3
  4. data/lib/fixturama.rb +49 -20
  5. data/lib/fixturama/changes.rb +72 -0
  6. data/lib/fixturama/changes/base.rb +28 -0
  7. data/lib/fixturama/changes/chain.rb +64 -0
  8. data/lib/fixturama/changes/chain/actions.rb +31 -0
  9. data/lib/fixturama/changes/chain/arguments.rb +59 -0
  10. data/lib/fixturama/changes/chain/raise_action.rb +43 -0
  11. data/lib/fixturama/changes/chain/return_action.rb +33 -0
  12. data/lib/fixturama/changes/const.rb +30 -0
  13. data/lib/fixturama/changes/env.rb +35 -0
  14. data/lib/fixturama/changes/request.rb +91 -0
  15. data/lib/fixturama/changes/request/response.rb +62 -0
  16. data/lib/fixturama/changes/request/responses.rb +22 -0
  17. data/lib/fixturama/changes/seed.rb +39 -0
  18. data/lib/fixturama/config.rb +5 -0
  19. data/lib/fixturama/fixture_error.rb +31 -0
  20. data/lib/fixturama/loader.rb +1 -0
  21. data/lib/fixturama/loader/context.rb +1 -0
  22. data/lib/fixturama/loader/value.rb +1 -0
  23. data/lib/fixturama/version.rb +4 -0
  24. metadata +80 -50
  25. data/.gitignore +0 -12
  26. data/.rspec +0 -2
  27. data/.rubocop.yml +0 -22
  28. data/.travis.yml +0 -23
  29. data/Gemfile +0 -9
  30. data/LICENSE.txt +0 -21
  31. data/Rakefile +0 -6
  32. data/fixturama.gemspec +0 -23
  33. data/lib/fixturama/seed.rb +0 -15
  34. data/lib/fixturama/stubs.rb +0 -57
  35. data/lib/fixturama/stubs/chain.rb +0 -89
  36. data/lib/fixturama/stubs/chain/actions.rb +0 -39
  37. data/lib/fixturama/stubs/chain/actions/raise.rb +0 -21
  38. data/lib/fixturama/stubs/chain/actions/return.rb +0 -23
  39. data/lib/fixturama/stubs/chain/arguments.rb +0 -86
  40. data/lib/fixturama/stubs/const.rb +0 -42
  41. data/lib/fixturama/utils.rb +0 -39
  42. data/spec/fixturama/load_fixture/_spec.rb +0 -53
  43. data/spec/fixturama/load_fixture/data.json +0 -5
  44. data/spec/fixturama/load_fixture/data.yaml +0 -3
  45. data/spec/fixturama/load_fixture/data.yml +0 -3
  46. data/spec/fixturama/seed_fixture/_spec.rb +0 -33
  47. data/spec/fixturama/seed_fixture/seed.yml +0 -16
  48. data/spec/fixturama/stub_fixture/_spec.rb +0 -96
  49. data/spec/fixturama/stub_fixture/stub.yml +0 -60
  50. data/spec/spec_helper.rb +0 -26
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 64d9722c641abfd4aa64fbf45b8f1177f768d1b8d2b99a89aa824e1f64db9417
4
- data.tar.gz: e1186c8944916943bbab46584747553b07b9d5cfc4e9d875d1a401fc8bd7017a
3
+ metadata.gz: 779a1dc8057604e6a5524eff0ef07520ecdbaf62895711411a312fb5c81a7558
4
+ data.tar.gz: 7976718e10f2fe8036d9e934d6e720cdcb91890f4263221720bfe76815eca071
5
5
  SHA512:
6
- metadata.gz: 29d44cd1b06c482ba40df2d4397c064888a64ae1f9a0a4151aa581bf81a83e9ca1a5e84c052340d515032d0f580f11418f9838a5efe00fd722401117248a0311
7
- data.tar.gz: 2b8a86f7fffdb20bae92839790254e052d2dbbbb39172003c9cfb4439620c782e4bc3f2ccdd985540dcba4d19e1fc228a3f7d940d58a865ad289f2f265427589
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
- Another opinionated format we use for stubs (`stub_fixture`). The gem supports stubbing both message chains and constants.
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
- Every action either `return` some value, or `raise` some exception
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/stubs"
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
- def stub_fixture(path, **opts)
20
- items = load_fixture(path, **opts)
21
- raise "The fixture should contain an array" unless items.is_a?(Array)
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
- items.each { |item| fixturama_stubs.add(item) }
24
- fixturama_stubs.apply(self)
37
+ ERB.new(content).result(bindings)
25
38
  end
26
39
 
27
- def seed_fixture(path, **opts)
28
- Array(load_fixture(path, **opts)).each { |item| Seed.call(item) }
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
- def load_fixture(path, **opts)
32
- Loader.new(path, opts).call
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
- def read_fixture(path, **opts)
36
- content = File.read(path)
37
- hashie = Hashie::Mash.new(opts)
38
- bindings = hashie.instance_eval { binding }
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
- ERB.new(content).result(bindings)
41
- end
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 fixturama_stubs
46
- @fixturama_stubs ||= Stubs.new
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