fixturama 0.1.0 → 0.5.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.
Files changed (53) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +99 -0
  3. data/README.md +76 -4
  4. data/lib/fixturama.rb +48 -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 +6 -2
  21. data/lib/fixturama/loader/context.rb +14 -5
  22. data/lib/fixturama/loader/value.rb +1 -0
  23. data/lib/fixturama/version.rb +4 -0
  24. metadata +85 -73
  25. data/.gitignore +0 -12
  26. data/.rspec +0 -2
  27. data/.rubocop.yml +0 -22
  28. data/.travis.yml +0 -17
  29. data/Gemfile +0 -9
  30. data/LICENSE.txt +0 -21
  31. data/Rakefile +0 -6
  32. data/fixturama.gemspec +0 -24
  33. data/lib/fixturama/seed.rb +0 -15
  34. data/lib/fixturama/stubs.rb +0 -79
  35. data/lib/fixturama/stubs/chain.rb +0 -90
  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 -43
  41. data/lib/fixturama/stubs/request.rb +0 -98
  42. data/lib/fixturama/stubs/request/response.rb +0 -43
  43. data/lib/fixturama/stubs/request/responses.rb +0 -20
  44. data/lib/fixturama/utils.rb +0 -39
  45. data/spec/fixturama/load_fixture/_spec.rb +0 -53
  46. data/spec/fixturama/load_fixture/data.json +0 -5
  47. data/spec/fixturama/load_fixture/data.yaml +0 -3
  48. data/spec/fixturama/load_fixture/data.yml +0 -3
  49. data/spec/fixturama/seed_fixture/_spec.rb +0 -36
  50. data/spec/fixturama/seed_fixture/seed.yml +0 -16
  51. data/spec/fixturama/stub_fixture/_spec.rb +0 -120
  52. data/spec/fixturama/stub_fixture/stub.yml +0 -73
  53. data/spec/spec_helper.rb +0 -26
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 5bfb62ba99d427f8a3f03b6d46a0fb7e6712dd31bfff6da7c17fb075cfa930f0
4
- data.tar.gz: fa1b0359c0681a73723a915dd499945602b350b3b802b68e6962a66753a5d208
3
+ metadata.gz: 4e21218a92d62cab4b0bbc73fe1f2ac02eb5c66a7315357a68189067d138d92a
4
+ data.tar.gz: 81861ef1dbf1eba81874fb929875c89f04dc3ebdeba9694ae02989762dd7aca5
5
5
  SHA512:
6
- metadata.gz: 987b0cf303e78879b18278d85ace7cf26261e6c76000b0081571cb40a3dcf763435cb5560de122bf3a466b9c781684c8ed8d183697fbd1b84aa619c8409e34c8
7
- data.tar.gz: d85486458d90dbff1e3f2136c5862e081aa1a1861bb5e6320fb68bbc258d11ce1e978de5603c900080ef719809e97a4d0bf793a46c8f90af33e7a6626d258835
6
+ metadata.gz: f09063278691d752987cc1b9bbc8d20a715624cce81f81e9c61b521d52af28b9fbe03e09ac9357e01c154b987608558231b3c0ca8d1bdd5677303f887630b3ad
7
+ data.tar.gz: b93be44d444875df3d793bb7947bbd9c906a0fcc699c3b1ec4625186a38eed3b58b8a11f11d6e7ae421db4615534533029c2fece7d592a357649ef24598e63a9
data/CHANGELOG.md CHANGED
@@ -5,6 +5,100 @@ 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.5.0] - [2021-04-03]
9
+
10
+ ### Added
11
+
12
+ - Support for <de>serialization PORO objects (nepalez)
13
+
14
+ ```yaml
15
+ # target.yml
16
+ ---
17
+ number: <%= object(be_positive) %>
18
+ ```
19
+
20
+ ```ruby
21
+ RSpec.describe "something" do
22
+ subject { { "number" => 42 } }
23
+
24
+ # no explicit params is needed here
25
+ let(:target) { load_fixture "target.yml" }
26
+
27
+ it { is_expected.to match(target) }
28
+ end
29
+ ```
30
+
31
+ ## [0.4.1] - [2021-03-31]
32
+
33
+ ### Fixed
34
+
35
+ - Dependency from hashie updated to allow v4+ (nepalez)
36
+
37
+ ## [0.4.0] - [2020-03-15]
38
+
39
+ ### Added
40
+
41
+ - Support for stubbing ENV variables (nepalez)
42
+
43
+ ```yaml
44
+ ---
45
+ - env: GOOGLE_CLOUD_KEY
46
+ value: foo
47
+
48
+ - env: GOOGLE_CLOUD_PASSWORD
49
+ value: bar
50
+ ```
51
+
52
+ This would stub selected variables only, not touching the others
53
+
54
+ ## [0.3.0] - [2020-03-08]
55
+
56
+ ### Added
57
+
58
+ - Support for exception arguments (nepalez)
59
+
60
+ ```yaml
61
+ ---
62
+ - class: API
63
+ chain: get_product
64
+ arguments:
65
+ - 1
66
+ actions:
67
+ - raise: API::NotFoundError
68
+ arguments: # <--- that's that
69
+ - "Cannot find a product by id: 1"
70
+ ```
71
+
72
+ which would raise `API::NotFoundError.new("Cannot find a product by id: 1")`
73
+
74
+ ## [0.2.0] - [2020-02-17]
75
+
76
+ ### Added
77
+
78
+ - Stubbing and seeding from the same source file via the `call_fixture` method (nepalez)
79
+
80
+ ```yaml
81
+ # ./changes.yml
82
+ ---
83
+ - type: user
84
+ params:
85
+ id: 1
86
+
87
+ - const: DEFAULT_USER_ID
88
+ value: 1
89
+
90
+ - url: https://example.com/users/default
91
+ method: get
92
+ responses:
93
+ - body:
94
+ id: 1
95
+ name: Andrew
96
+ ```
97
+
98
+ ```ruby
99
+ before { call_fixture "#{__dir__}/changes.yml" }
100
+ ```
101
+
8
102
  ## [0.1.0] - [2020-02-09]
9
103
 
10
104
  ### Added
@@ -200,3 +294,8 @@ This is a first public release with features extracted from production app.
200
294
  [0.0.6]: https://github.com/nepalez/fixturama/compare/v0.0.5...v0.0.6
201
295
  [0.0.7]: https://github.com/nepalez/fixturama/compare/v0.0.6...v0.0.7
202
296
  [0.1.0]: https://github.com/nepalez/fixturama/compare/v0.0.7...v0.1.0
297
+ [0.2.0]: https://github.com/nepalez/fixturama/compare/v0.1.0...v0.2.0
298
+ [0.3.0]: https://github.com/nepalez/fixturama/compare/v0.2.0...v0.3.0
299
+ [0.4.0]: https://github.com/nepalez/fixturama/compare/v0.3.0...v0.4.0
300
+ [0.4.1]: https://github.com/nepalez/fixturama/compare/v0.4.0...v0.4.1
301
+ [0.5.0]: https://github.com/nepalez/fixturama/compare/v0.4.1...v0.5.0
data/README.md CHANGED
@@ -99,6 +99,38 @@ This feature can also be useful to produce a "partially defined" fixtures with [
99
99
  subject { load_fixture "#{__dir__}/data.yml", user: kind_of(ActiveRecord::Base) }
100
100
  ```
101
101
 
102
+ Since the v0.5.0 we support another way to serialize PORO objects in fixtures. Just wrap them to the `object()` method:
103
+
104
+ ```yaml
105
+ ---
106
+ :account: <%= object(user) %>
107
+ ```
108
+
109
+ This time you don't need sending objects explicitly.
110
+
111
+ ```ruby
112
+ RSpec.describe "example" do
113
+ subject { load_fixture "#{__dir__}/data.yml" }
114
+
115
+ let(:user) { FactoryBot.create(:user) }
116
+
117
+ # The same object will be returned
118
+ it { is_expected.to eq(account: user) }
119
+ end
120
+ ```
121
+
122
+ Under the hood we use `Marshal.dump` and `Marshal.restore` to serialize and deserialize the object back.
123
+
124
+ **Notice**, that deserialization creates a new instance of the object which is not equivalent to the source (`user` in the example above)!
125
+ In most cases this is enough. For example, you can provide matchers like:
126
+
127
+ ```yaml
128
+ ---
129
+ number: <%= object(be_positive) %>
130
+ ```
131
+
132
+ The loaded object would contain `{ "number" => be_positive }`.
133
+
102
134
  ### Seeding
103
135
 
104
136
  The seed (`seed_fixture`) file should be a YAML/JSON with opinionated parameters, namely:
@@ -124,22 +156,28 @@ Use the `count: 2` key to create more objects at once.
124
156
 
125
157
  ### Stubbing
126
158
 
127
- Another opinionated format we use for stubs (`stub_fixture`). The gem supports stubbing both message chains and constants.
159
+ The gem supports stubbing message chains, constants and http requests with the following keys.
128
160
 
129
161
  For message chains:
130
162
 
131
163
  - `class` for stubbed class
132
164
  - `chain` for messages chain
133
165
  - `arguments` (optional) for specific arguments
134
- - `actions` for an array of actions for consecutive invocations of the chain
135
-
136
- Every action either `return` some value, or `raise` some exception
166
+ - `actions` for an array of actions for consecutive invocations of the chain with keys
167
+ - `return` for a value to be returned
168
+ - `raise` for an exception to be risen
169
+ - `repeate` for a number of invocations with this action
137
170
 
138
171
  For constants:
139
172
 
140
173
  - `const` for stubbed constant
141
174
  - `value` for a value of the constant
142
175
 
176
+ For environment variables:
177
+
178
+ - `env` for the name of a variable
179
+ `value` for a value of the variable
180
+
143
181
  For http requests:
144
182
 
145
183
  - `url` or `uri` for the URI of the request (treats values like `/.../` as regular expressions)
@@ -152,6 +190,7 @@ For http requests:
152
190
  - `status`
153
191
  - `body`
154
192
  - `headers`
193
+ - `repeate` for the number of times this response should be returned before switching to the next one
155
194
 
156
195
  ```yaml
157
196
  # ./stubs.yml
@@ -179,11 +218,19 @@ For http requests:
179
218
  - <%= profile_id %>
180
219
  actions:
181
220
  - return: true
221
+ repeate: 1 # this is the default value
182
222
  - raise: ActiveRecord::RecordNotFound
223
+ arguments:
224
+ - "Profile with id: 1 not found" # for error message
183
225
 
226
+ # Here we stubbing a constant
184
227
  - const: NOTIFIER_TIMEOUT_SEC
185
228
  value: 10
186
229
 
230
+ # This is a stub for ENV['DEFAULT_EMAIL']
231
+ - env: DEFAULT_EMAIL
232
+ value: foo@example.com
233
+
187
234
  # Examples for stubbing HTTP
188
235
  - uri: /example.com/foo/ # regexp!
189
236
  method: delete
@@ -192,6 +239,7 @@ For http requests:
192
239
  password: bar
193
240
  responses:
194
241
  - status: 200 # for the first call
242
+ repeate: 1 # this is the default value, but you can set another one
195
243
  - status: 404 # for any other call
196
244
 
197
245
  - uri: htpps://example.com/foo # exact string!
@@ -231,6 +279,30 @@ I find it especially helpful when I need to check different edge cases. Instead
231
279
 
232
280
  Looking at the spec I can easily figure out the "structure" of expectation, while looking at fixtures I can check the concrete corner cases.
233
281
 
282
+ ## Single Source of Changes
283
+
284
+ If you will, you can list all stubs and seeds at the one single file like
285
+
286
+ ```yaml
287
+ # ./changes.yml
288
+ ---
289
+ - type: user
290
+ params:
291
+ id: 1
292
+ name: Andrew
293
+
294
+ - const: DEFAULT_USER_ID
295
+ value: 1
296
+ ```
297
+
298
+ This fixture can be applied via `call_fixture` method just like we did above with `seed_fixture` and `stub_fixture`:
299
+
300
+ ```ruby
301
+ before { call_fixture "#{__dir__}/changes.yml" }
302
+ ```
303
+
304
+ In fact, since the `v0.2.0` all those methods are just the aliases of the `call_fixture`.
305
+
234
306
  ## License
235
307
 
236
308
  The gem is available as open source under the terms of the [MIT License][license].
data/lib/fixturama.rb CHANGED
@@ -6,44 +6,72 @@ require "rspec"
6
6
  require "webmock/rspec"
7
7
  require "yaml"
8
8
 
9
+ #
10
+ # A set of helpers to prettify specs with fixtures
11
+ #
9
12
  module Fixturama
13
+ require_relative "fixturama/fixture_error"
10
14
  require_relative "fixturama/config"
11
- require_relative "fixturama/utils"
12
15
  require_relative "fixturama/loader"
13
- require_relative "fixturama/stubs"
14
- require_relative "fixturama/seed"
16
+ require_relative "fixturama/changes"
15
17
 
18
+ # Set the initial value for database-generated IDs
19
+ # @param [#to_i] value
20
+ # @return [Fixturama]
16
21
  def self.start_ids_from(value)
17
22
  Config.start_ids_from(value)
23
+ self
18
24
  end
19
25
 
20
- def stub_fixture(path, **opts)
21
- items = load_fixture(path, **opts)
22
- 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 }
23
36
 
24
- items.each { |item| fixturama_stubs.add(item) }
25
- fixturama_stubs.apply(self)
37
+ ERB.new(content).result(bindings)
26
38
  end
27
39
 
28
- def seed_fixture(path, **opts)
29
- 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(self, path, options).call
30
46
  end
31
47
 
32
- def load_fixture(path, **opts)
33
- 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)
34
58
  end
35
59
 
36
- def read_fixture(path, **opts)
37
- content = File.read(path)
38
- hashie = Hashie::Mash.new(opts)
39
- 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
40
65
 
41
- ERB.new(content).result(bindings)
42
- 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
43
71
 
44
72
  private
45
73
 
46
- def fixturama_stubs
47
- @fixturama_stubs ||= Stubs.new
74
+ def changes
75
+ @changes ||= Changes.new
48
76
  end
49
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.instance_of?(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