grift 2.0.1 → 2.1.0

Sign up to get free protection for your applications and to get access to all the features.
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