grift 2.0.1 → 2.1.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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: '06800e91cb7e76be9232c7cf700d99ede4ba25212ed323abd1d5a4e944de455d'
4
- data.tar.gz: 2d844fc80a5edcb9eb809c4946d7ee464465e8423b7a42e2f3a4ff4657e791d7
3
+ metadata.gz: be076a2312ee3278d68f393d8ace60b12aa44cec9923336fbe0469366be5c9cd
4
+ data.tar.gz: b38846285534aaf9e56c413c77b84da307655a1ac336aba9c8a4fa3b676a34c7
5
5
  SHA512:
6
- metadata.gz: c3a2e6a02bab0921a5c91c48c5fbbf67d1a9dd0b0d50bd3c4d9dfb49d6f686623ca0c017d1d51f1df3220b4c1c83c1fd9a26070daa05c36c38d657a2476a4602
7
- data.tar.gz: a7197e5fb4f7bbaf506426c9f4ed2a04733201f8a27a4f015df9af7466c9bd8563e700c2adaaef34211690c5ecf795ded15308701877bd5a0fbb6ed5278b366a
6
+ metadata.gz: 25cdd247a6c4eacff1c77b099d22475bedb56d7a885beb1365e71e6798bddee87401fe1257e65cfbb5af5137b8458af650f2916f32b000a0712c3494e3f45459
7
+ data.tar.gz: 2c0e207ea0c7eb8db2719af429aaf0831fcb18169f5167d92f0111207e5a228e9f2a5d49131565dd499043268b65c4481fbd4497dca918c993326d7ed683102f
@@ -15,12 +15,12 @@ jobs:
15
15
  BUNDLE_WITHOUT: development:test
16
16
 
17
17
  steps:
18
- - uses: actions/checkout@v2
18
+ - uses: actions/checkout@v3
19
19
 
20
- - name: Set up Ruby 2.7
20
+ - name: Set up Ruby 3.1
21
21
  uses: ruby/setup-ruby@v1
22
22
  with:
23
- ruby-version: 2.7
23
+ ruby-version: 3.1
24
24
  bundler-cache: true
25
25
 
26
26
  - name: Rubocop
@@ -33,10 +33,10 @@ jobs:
33
33
  strategy:
34
34
  fail-fast: false
35
35
  matrix:
36
- ruby: ["2.7", "3.0", "3.1"]
36
+ ruby: ["2.7", "3.0", "3.1", "3.2"]
37
37
 
38
38
  steps:
39
- - uses: actions/checkout@v2
39
+ - uses: actions/checkout@v3
40
40
  - uses: ruby/setup-ruby@v1
41
41
  with:
42
42
  ruby-version: ${{ matrix.ruby }}
@@ -47,7 +47,7 @@ jobs:
47
47
  run: bundle exec rake test
48
48
 
49
49
  - name: Upload coverage to Codecov
50
- uses: codecov/codecov-action@v2
50
+ uses: codecov/codecov-action@v3
51
51
  with:
52
52
  token: ${{ secrets.CODECOV_TOKEN }}
53
53
  files: ./coverage/.resultset.json
data/.rubocop.yml CHANGED
@@ -102,12 +102,12 @@ Lint/AmbiguousRange:
102
102
 
103
103
  # Metrics
104
104
  Metrics/AbcSize:
105
- Max: 20
105
+ Max: 25
106
106
  Exclude:
107
107
  - "test/**/*"
108
108
 
109
109
  Metrics/ClassLength:
110
- Max: 150
110
+ Max: 225
111
111
  CountAsOne:
112
112
  - array
113
113
  - hash
@@ -128,6 +128,9 @@ Metrics/MethodLength:
128
128
  Minitest/AssertPredicate:
129
129
  Enabled: false
130
130
 
131
+ Minitest/EmptyLineBeforeAssertionMethods:
132
+ Enabled: false
133
+
131
134
  Minitest/MultipleAssertions:
132
135
  Max: 10
133
136
 
@@ -138,6 +141,10 @@ Minitest/RefutePredicate:
138
141
  Naming/InclusiveLanguage:
139
142
  Enabled: true
140
143
 
144
+ Naming/MethodParameterName:
145
+ AllowedNames:
146
+ - n
147
+
141
148
  Naming/VariableNumber:
142
149
  EnforcedStyle: snake_case
143
150
 
data/CHANGELOG.md CHANGED
@@ -8,6 +8,15 @@ This project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.htm
8
8
 
9
9
  None
10
10
 
11
+ ## [2.1.0](https://github.com/clarkedb/grift/releases/tag/v2.1.0) - 2022-12-27
12
+
13
+ ### Added
14
+
15
+ * Official support for Ruby 3.2 ([#126](https://github.com/clarkedb/grift/pull/126))
16
+ * Support for finite/self-terminating mocking for more precision in testing where a method may get called multiple times
17
+ + The Grift API now supports `mock_return_value_once`, `mock_return_value_n_times`, and `mock_return_values_in_order` ([#135](https://github.com/clarkedb/grift/pull/135))
18
+ + The Grift API now supports `mock_implementation_once` and `mock_implementation_n_times` ([#136](https://github.com/clarkedb/grift/pull/136))
19
+
11
20
  ## [2.0.1](https://github.com/clarkedb/grift/releases/tag/v2.0.1) - 2022-03-27
12
21
 
13
22
  ### Fixed
data/Gemfile.lock CHANGED
@@ -1,7 +1,7 @@
1
1
  PATH
2
2
  remote: .
3
3
  specs:
4
- grift (2.0.1)
4
+ grift (2.1.0)
5
5
 
6
6
  GEM
7
7
  remote: https://rubygems.org/
@@ -10,53 +10,56 @@ GEM
10
10
  ast (2.4.2)
11
11
  builder (3.2.4)
12
12
  childprocess (4.1.0)
13
- codecov (0.6.0)
14
- simplecov (>= 0.15, < 0.22)
13
+ codecov (0.2.12)
14
+ json
15
+ simplecov
15
16
  docile (1.4.0)
16
17
  iniparse (1.5.0)
17
- minitest (5.15.0)
18
+ json (2.6.3)
19
+ minitest (5.16.3)
18
20
  minitest-reporters (1.5.0)
19
21
  ansi
20
22
  builder
21
23
  minitest (>= 5.0)
22
24
  ruby-progressbar
23
- overcommit (0.58.0)
25
+ overcommit (0.59.1)
24
26
  childprocess (>= 0.6.3, < 5)
25
27
  iniparse (~> 1.4)
26
28
  rexml (~> 3.2)
27
- parallel (1.21.0)
28
- parser (3.1.1.0)
29
+ parallel (1.22.1)
30
+ parser (3.1.3.0)
29
31
  ast (~> 2.4.1)
30
32
  rainbow (3.1.1)
31
33
  rake (13.0.6)
32
- regexp_parser (2.2.1)
34
+ regexp_parser (2.6.1)
33
35
  rexml (3.2.5)
34
- rubocop (1.26.0)
36
+ rubocop (1.41.1)
37
+ json (~> 2.3)
35
38
  parallel (~> 1.10)
36
- parser (>= 3.1.0.0)
39
+ parser (>= 3.1.2.1)
37
40
  rainbow (>= 2.2.2, < 4.0)
38
41
  regexp_parser (>= 1.8, < 3.0)
39
- rexml
40
- rubocop-ast (>= 1.16.0, < 2.0)
42
+ rexml (>= 3.2.5, < 4.0)
43
+ rubocop-ast (>= 1.23.0, < 2.0)
41
44
  ruby-progressbar (~> 1.7)
42
45
  unicode-display_width (>= 1.4.0, < 3.0)
43
- rubocop-ast (1.16.0)
46
+ rubocop-ast (1.24.0)
44
47
  parser (>= 3.1.1.0)
45
- rubocop-minitest (0.18.0)
48
+ rubocop-minitest (0.25.1)
46
49
  rubocop (>= 0.90, < 2.0)
47
- rubocop-packaging (0.5.1)
48
- rubocop (>= 0.89, < 2.0)
49
- rubocop-performance (1.13.3)
50
+ rubocop-packaging (0.5.2)
51
+ rubocop (>= 1.33, < 2.0)
52
+ rubocop-performance (1.15.2)
50
53
  rubocop (>= 1.7.0, < 2.0)
51
54
  rubocop-ast (>= 0.4.0)
52
55
  ruby-progressbar (1.11.0)
53
- simplecov (0.21.2)
56
+ simplecov (0.22.0)
54
57
  docile (~> 1.1)
55
58
  simplecov-html (~> 0.11)
56
59
  simplecov_json_formatter (~> 0.1)
57
60
  simplecov-html (0.12.3)
58
- simplecov_json_formatter (0.1.3)
59
- unicode-display_width (2.1.0)
61
+ simplecov_json_formatter (0.1.4)
62
+ unicode-display_width (2.3.0)
60
63
 
61
64
  PLATFORMS
62
65
  ruby
data/README.md CHANGED
@@ -1,7 +1,6 @@
1
1
  # Grift
2
2
 
3
3
  [![gem version](https://badge.fury.io/rb/grift.svg)](https://rubygems.org/gems/grift)
4
- [![downloads](https://ruby-gem-downloads-badge.herokuapp.com/grift)](https://rubygems.org/gems/grift)
5
4
  [![build](https://github.com/clarkedb/grift/actions/workflows/ci.yml/badge.svg)](https://github.com/clarkedb/grift/actions?query=workflow%3ACI)
6
5
  [![codecov](https://codecov.io/gh/clarkedb/grift/branch/main/graph/badge.svg)](https://codecov.io/gh/clarkedb/grift)
7
6
 
@@ -123,7 +122,7 @@ my_mock.mock.results
123
122
 
124
123
  ## Requirements
125
124
 
126
- Grift supports all Ruby versions >= 2.7 (including 3.1).
125
+ Grift supports all Ruby versions >= 2.7 (including 3.2).
127
126
 
128
127
  ## Development
129
128
 
@@ -145,7 +145,116 @@ module Grift
145
145
 
146
146
  # record the args passed in the call to the method and the result
147
147
  mock_executions.store(args: args, result: return_value)
148
- return return_value
148
+ return_value
149
+ end
150
+ class_instance.send(@method_access, @method_name)
151
+
152
+ self
153
+ end
154
+
155
+ ##
156
+ # Accepts a block and mocks the method to execute that block instead
157
+ # of the original behavior the next time the method is called while mocked.
158
+ # After the method has been called once, it will return to its original
159
+ # behavior. The method will continue to be watched.
160
+ #
161
+ # @see #mock_implementation
162
+ #
163
+ # @example
164
+ # my_mock = Grift.spy_on(String, :downcase).mock_implementation_once do
165
+ # x = 3 + 4
166
+ # x.to_s
167
+ # end
168
+ # ["Banana", "Apple"].map(&:downcase)
169
+ # #=> ["7", "apple"]
170
+ #
171
+ # @return [self] the mock itself
172
+ #
173
+ def mock_implementation_once(*)
174
+ raise(Grift::Error, 'Must provide a block for the new implementation') unless block_given?
175
+
176
+ premock_setup
177
+
178
+ # required to access inside class instance block
179
+ mock_executions = @mock_executions
180
+ clean_mock = lambda do
181
+ unmock_method
182
+ watch_method
183
+ end
184
+
185
+ class_instance.remove_method(@method_name) if !@inherited && method_defined?
186
+ class_instance.define_method @method_name do |*args, **kwargs|
187
+ return_value = yield(*args, **kwargs)
188
+
189
+ # record the args passed in the call to the method and the result
190
+ mock_executions.store(args: args, result: return_value)
191
+
192
+ clean_mock.call
193
+
194
+ return_value
195
+ end
196
+ class_instance.send(@method_access, @method_name)
197
+
198
+ self
199
+ end
200
+
201
+ ##
202
+ # Accepts a number +n+ and a block and mocks the method to execute that block
203
+ # instaead of the original behavior the next +n+ times the method is called
204
+ # while mocked. After the method has been called once, it will return to its
205
+ # original behavior. The method will continue to be watched.
206
+ #
207
+ # **IMPORANT:** Calling {#mock_clear} clears the method call history. If it is
208
+ # called before the nth execution of the mocked method, the method will remain
209
+ # mocked for an additonal +n+ calls.
210
+ #
211
+ # @see #mock_implementation
212
+ #
213
+ # @example
214
+ # my_mock = Grift.spy_on(String, :downcase).mock_implementation_n_times(3) do
215
+ # x = 3 + 4
216
+ # x.to_s
217
+ # end
218
+ # ["Banana", "Apple", "Orange", "Guava"].map(&:downcase)
219
+ # #=> ["7", "7", "7", "guava"]
220
+ #
221
+ # @example
222
+ # my_mock = Grift.spy_on(String, :downcase).mock_implementation_n_times(5) do
223
+ # x = 3 + 4
224
+ # x.to_s
225
+ # end
226
+ # ["Banana", "Apple", "Orange", "Guava"].map(&:downcase)
227
+ # #=> ["7", "7", "7", "7"]
228
+ # my_mock.mock_clear # clear mock history before 5th (nth) method call
229
+ # ["Banana", "Apple", "Orange", "Guava"].map(&:downcase)
230
+ # #=> ["7", "7", "7", "7"]
231
+ #
232
+ # @param n [Number] the number of times to mock the implementation
233
+ #
234
+ # @return [self] the mock itself
235
+ #
236
+ def mock_implementation_n_times(n, *)
237
+ raise(Grift::Error, 'Must provide a block for the new implementation') unless block_given?
238
+
239
+ premock_setup
240
+
241
+ # required to access inside class instance block
242
+ mock_executions = @mock_executions
243
+ clean_mock = lambda do
244
+ unmock_method
245
+ watch_method
246
+ end
247
+
248
+ class_instance.remove_method(@method_name) if !@inherited && method_defined?
249
+ class_instance.define_method @method_name do |*args, **kwargs|
250
+ return_value = yield(*args, **kwargs)
251
+
252
+ # record the args passed in the call to the method and the result
253
+ mock_executions.store(args: args, result: return_value)
254
+
255
+ clean_mock.call if mock_executions.count == n
256
+
257
+ return_value
149
258
  end
150
259
  class_instance.send(@method_access, @method_name)
151
260
 
@@ -175,7 +284,144 @@ module Grift
175
284
  class_instance.define_method @method_name do |*args, **kwargs|
176
285
  # record the args passed in the call to the method and the result
177
286
  mock_executions.store(args: args, kwargs: kwargs, result: return_value)
178
- return return_value
287
+ return_value
288
+ end
289
+ class_instance.send(@method_access, @method_name)
290
+
291
+ self
292
+ end
293
+
294
+ ##
295
+ # Accepts a value and mocks the method to return that value once instead
296
+ # of executing its original behavior while mocked. After the method has
297
+ # been called once, it will return to its original behavior. The method
298
+ # will continue to be watched.
299
+ #
300
+ # @example
301
+ # my_mock = Grift.spy_on(String, :upcase).mock_return_value_once("BANANA")
302
+ # ["apple", "apple"].map(&:upcase)
303
+ # #=> ["BANANA", "APPLE"]
304
+ #
305
+ # @param return_value the value to return from the method once
306
+ #
307
+ # @return [self] the mock itself
308
+ #
309
+ def mock_return_value_once(return_value = nil)
310
+ premock_setup
311
+
312
+ # required to access mock inside class instance block
313
+ mock_executions = @mock_executions
314
+ clean_mock = lambda do
315
+ unmock_method
316
+ watch_method
317
+ end
318
+
319
+ class_instance.remove_method(@method_name) if !@inherited && method_defined?
320
+ class_instance.define_method @method_name do |*args, **kwargs|
321
+ # record the args passed in the call to the method and the result
322
+ mock_executions.store(args: args, kwargs: kwargs, result: return_value)
323
+
324
+ clean_mock.call
325
+
326
+ return_value
327
+ end
328
+ class_instance.send(@method_access, @method_name)
329
+
330
+ self
331
+ end
332
+
333
+ ##
334
+ # Accepts a value and mocks the method to return that value +n+ times instead
335
+ # of executing its original behavior while mocked. After the method has
336
+ # been called +n+ times, it will return to its original behavior. The method
337
+ # will continue to be watched.
338
+ #
339
+ # **IMPORANT:** Calling {#mock_clear} clears the method call history. If it is
340
+ # called before the nth execution of the mocked method, the method will remain
341
+ # mocked for an additional +n+ calls.
342
+ #
343
+ # @example
344
+ # my_mock = Grift.spy_on(String, :upcase).mock_return_value_n_times(2, "BANANA")
345
+ # ["apple", "apple", "apple"].map(&:upcase)
346
+ # #=> ["BANANA", "BANANA", "APPLE"]
347
+ #
348
+ # @example
349
+ # my_mock = Grift.spy_on(String, :upcase).mock_return_value_n_times(4, "BANANA")
350
+ # ["apple", "apple", "apple"].map(&:upcase)
351
+ # #=> ["BANANA", "BANANA", "BANANA"]
352
+ # my_mock.mock_clear # clear mock history before 4th (nth) method call
353
+ # ["apple", "apple", "apple"].map(&:upcase)
354
+ # #=> ["BANANA", "BANANA", "BANANA"]
355
+ #
356
+ # @param n [Number] the number of times to mock the return value
357
+ # @param return_value the value to return from the method +n+ times
358
+ #
359
+ # @return [self] the mock itself
360
+ #
361
+ def mock_return_value_n_times(n, return_value = nil)
362
+ premock_setup
363
+
364
+ # required to access mock inside class instance block
365
+ mock_executions = @mock_executions
366
+ clean_mock = lambda do
367
+ unmock_method
368
+ watch_method
369
+ end
370
+
371
+ class_instance.remove_method(@method_name) if !@inherited && method_defined?
372
+ class_instance.define_method @method_name do |*args, **kwargs|
373
+ # record the args passed in the call to the method and the result
374
+ mock_executions.store(args: args, kwargs: kwargs, result: return_value)
375
+
376
+ clean_mock.call if mock_executions.count == n
377
+
378
+ return_value
379
+ end
380
+ class_instance.send(@method_access, @method_name)
381
+
382
+ self
383
+ end
384
+
385
+ ##
386
+ # Accepts an array of values and mocks the method to return those values
387
+ # in order instead of executing its original behavior while mocked. After
388
+ # the method has been called enough times to return each of the values,
389
+ # it will return to its original behavior. The method continue to be watched.
390
+ #
391
+ # @example
392
+ # mock_values = ["APPLE", "BANANA", "ORANGE"]
393
+ # my_mock = Grift.spy_on(String, :upcase).mock_return_values_in_order(mock_values)
394
+ # ["pineapple", "orange", "guava", "mango", "watermelon"].map(&:upcase)
395
+ # #=> ["APPLE", "BANANA", "ORANGE", "MANGO", "WATERMELON"]
396
+ #
397
+ # @param return_values [Array] the values to return from the method in order
398
+ #
399
+ # @return [self] the mock itself
400
+ #
401
+ def mock_return_values_in_order(return_values)
402
+ unless return_values.is_a?(Array) && !return_values.empty?
403
+ raise(Grift::Error, 'Must provide a non-empty array for the return values')
404
+ end
405
+
406
+ premock_setup
407
+
408
+ # required to access mock inside class instance block
409
+ mock_executions = @mock_executions
410
+ clean_mock = lambda do
411
+ unmock_method
412
+ watch_method
413
+ end
414
+ return_values_internal = return_values.dup
415
+
416
+ class_instance.remove_method(@method_name) if !@inherited && method_defined?
417
+ class_instance.define_method @method_name do |*args, **kwargs|
418
+ # record the args passed in the call to the method and the result
419
+ return_value = return_values_internal.shift
420
+ mock_executions.store(args: args, kwargs: kwargs, result: return_value)
421
+
422
+ clean_mock.call if return_values_internal.empty?
423
+
424
+ return_value
179
425
  end
180
426
  class_instance.send(@method_access, @method_name)
181
427
 
@@ -206,7 +452,7 @@ module Grift
206
452
  # @return [String] the hash of the class and method
207
453
  #
208
454
  def self.hash_key(klass, method_name)
209
- "#{klass}\##{method_name}"
455
+ "#{klass}##{method_name}"
210
456
  end
211
457
 
212
458
  private
@@ -227,7 +473,7 @@ module Grift
227
473
 
228
474
  # record the args passed in the call to the method and the result
229
475
  mock_executions.store(args: args, kwargs: kwargs, result: return_value)
230
- return return_value
476
+ return_value
231
477
  end
232
478
  class_instance.send(@method_access, @method_name)
233
479
 
data/lib/grift/version.rb CHANGED
@@ -2,6 +2,6 @@
2
2
 
3
3
  module Grift
4
4
  # gem version
5
- VERSION = '2.0.1'
5
+ VERSION = '2.1.0'
6
6
  public_constant :VERSION
7
7
  end
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: grift
3
3
  version: !ruby/object:Gem::Version
4
- version: 2.0.1
4
+ version: 2.1.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Clark Brown
8
8
  autorequire:
9
9
  bindir: exe
10
10
  cert_chain: []
11
- date: 2022-03-27 00:00:00.000000000 Z
11
+ date: 2022-12-27 00:00:00.000000000 Z
12
12
  dependencies: []
13
13
  description: A gem for simple mocking and spying in Ruby's MiniTest framework.
14
14
  email:
@@ -69,7 +69,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
69
69
  - !ruby/object:Gem::Version
70
70
  version: '0'
71
71
  requirements: []
72
- rubygems_version: 3.2.32
72
+ rubygems_version: 3.3.26
73
73
  signing_key:
74
74
  specification_version: 4
75
75
  summary: Mocking and spying in MiniTest