grift 1.0.2 → 2.0.1

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: 98c5113978ecfca383496652f50e2fee8b820bdf0f9dfd94e01649b567c44ee8
4
- data.tar.gz: 16d205bacdddd4018ef18654b09c7f9b7d283956a382875f6f4dbae58f1244dd
3
+ metadata.gz: '06800e91cb7e76be9232c7cf700d99ede4ba25212ed323abd1d5a4e944de455d'
4
+ data.tar.gz: 2d844fc80a5edcb9eb809c4946d7ee464465e8423b7a42e2f3a4ff4657e791d7
5
5
  SHA512:
6
- metadata.gz: 75ca213c47eabb2e1cf4ee8790949e041ff383193a844f34f1ab794486cd3afdf633e0206f367e7b74d358276bb43f48b7b0dcf8ae8fbb124977d42c34df1bb7
7
- data.tar.gz: 91e88879f44d036403d94808ab853007a66f10e60dad5569e38ace29f1969abc2abc24fdb87b6f9f5db61c98a8efdd34c6d4cfcb4f063fc147e9a1bc80ece52c
6
+ metadata.gz: c3a2e6a02bab0921a5c91c48c5fbbf67d1a9dd0b0d50bd3c4d9dfb49d6f686623ca0c017d1d51f1df3220b4c1c83c1fd9a26070daa05c36c38d657a2476a4602
7
+ data.tar.gz: a7197e5fb4f7bbaf506426c9f4ed2a04733201f8a27a4f015df9af7466c9bd8563e700c2adaaef34211690c5ecf795ded15308701877bd5a0fbb6ed5278b366a
@@ -4,8 +4,10 @@ updates:
4
4
  - directory: /
5
5
  open-pull-requests-limit: 10
6
6
  package-ecosystem: bundler
7
- target-branch: develop
7
+ target-branch: main
8
8
  schedule:
9
9
  interval: weekly
10
10
  labels:
11
11
  - dependencies
12
+ assignees:
13
+ - clarkedb
@@ -3,6 +3,7 @@ name: CI
3
3
  on: push
4
4
 
5
5
  env:
6
+ CI: true
6
7
  GIT_COMMIT_SHA: ${{ github.sha }}
7
8
  GIT_BRANCH: ${{ github.ref }}
8
9
 
@@ -26,13 +27,13 @@ jobs:
26
27
  run: bundle exec rubocop --format progress
27
28
 
28
29
  build:
29
- needs: [ linting ]
30
+ needs: [linting]
30
31
  runs-on: ubuntu-latest
31
32
  name: build (ruby v${{ matrix.ruby }})
32
33
  strategy:
33
34
  fail-fast: false
34
35
  matrix:
35
- ruby: [ '2.5', '2.6', '2.7', '3.0' ]
36
+ ruby: ["2.7", "3.0", "3.1"]
36
37
 
37
38
  steps:
38
39
  - uses: actions/checkout@v2
@@ -44,3 +45,12 @@ jobs:
44
45
  - name: Test
45
46
  continue-on-error: ${{ matrix.experimental }}
46
47
  run: bundle exec rake test
48
+
49
+ - name: Upload coverage to Codecov
50
+ uses: codecov/codecov-action@v2
51
+ with:
52
+ token: ${{ secrets.CODECOV_TOKEN }}
53
+ files: ./coverage/.resultset.json
54
+ name: codecov-umbrella
55
+ verbose: true
56
+ fail_ci_if_error: true
data/.overcommit.yml ADDED
@@ -0,0 +1,8 @@
1
+ PreCommit:
2
+ RuboCop:
3
+ enabled: true
4
+ on_warn: fail # Treat all warnings as failures
5
+
6
+ CommitMsg:
7
+ ALL:
8
+ enabled: false
data/.rubocop.yml CHANGED
@@ -9,6 +9,11 @@
9
9
  #
10
10
  # See https://docs.rubocop.org/rubocop/configuration
11
11
 
12
+ require:
13
+ - rubocop-minitest
14
+ - rubocop-packaging
15
+ - rubocop-performance
16
+
12
17
  # General
13
18
  AllCops:
14
19
  NewCops: enable
@@ -119,6 +124,16 @@ Metrics/MethodLength:
119
124
  Exclude:
120
125
  - "test/**/*"
121
126
 
127
+ # Minitest
128
+ Minitest/AssertPredicate:
129
+ Enabled: false
130
+
131
+ Minitest/MultipleAssertions:
132
+ Max: 10
133
+
134
+ Minitest/RefutePredicate:
135
+ Enabled: false
136
+
122
137
  # Naming
123
138
  Naming/InclusiveLanguage:
124
139
  Enabled: true
data/CHANGELOG.md CHANGED
@@ -6,7 +6,46 @@ This project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.htm
6
6
 
7
7
  ## Unreleased
8
8
 
9
- N/A
9
+ None
10
+
11
+ ## [2.0.1](https://github.com/clarkedb/grift/releases/tag/v2.0.1) - 2022-03-27
12
+
13
+ ### Fixed
14
+
15
+ * When spying on a method that takes a block, the block now gets forwarded to the original method ([#78](https://github.com/clarkedb/grift/pull/78))
16
+ * When mocking the implementation, if a block is not provided a `Grift::Error` is raised instead of a `LocalJumpError` ([#77](https://github.com/clarkedb/grift/pull/77))
17
+
18
+ ## [2.0.0](https://github.com/clarkedb/grift/releases/tag/v2.0.0) - 2022-03-14
19
+
20
+ This version adds true keyword argument support for Ruby 3. See below for how to handle breaking changes.
21
+
22
+ ### Changed
23
+
24
+ * Dropped support for Ruby 2.5 ([#69](https://github.com/clarkedb/grift/pull/69))
25
+ * Dropped support for Ruby 2.6 ([#72](https://github.com/clarkedb/grift/pull/72))
26
+ * To support keyword arguments, records of call arguments are no longer stored in simple arrays but in a custom Enumerable ([#72](https://github.com/clarkedb/grift/pull/72))
27
+ + This changes the way that your tests will interact with mock calls
28
+ + When before `calls` returned an array, it returns a `Grift::MockMethod::MockExecutions::MockArguments` object
29
+ + Migrating to maintain previous behavior just requires appending `.args` to `calls[i]`
30
+
31
+ ### Added
32
+
33
+ * Support for mocking private instance and class methods ([#68](https://github.com/clarkedb/grift/pull/68))
34
+ * Support for mocking methods that take positional and keyword arguments ([#72](https://github.com/clarkedb/grift/pull/72))
35
+
36
+ ### Fixed
37
+
38
+ * When mocking protected methods, the method now remains protected while mocked and after unmocking ([#68](https://github.com/clarkedb/grift/pull/68))
39
+ * When mocking inherited methods, the method goes back to the ancestor's definition after unmocking ([#69](https://github.com/clarkedb/grift/pull/69))
40
+ * When mocking methods with keyword arugments in Ruby 3.x, no error is thrown ([#72](https://github.com/clarkedb/grift/pull/72))
41
+
42
+ ## [1.1.0](https://github.com/clarkedb/grift/releases/tag/v1.1.0) - 2022-02-03
43
+
44
+ This version adds support for Ruby 3.1 and updates various dependencies.
45
+
46
+ ### Added
47
+
48
+ * Support Ruby 3.1 ([#52](https://github.com/clarkedb/grift/pull/52))
10
49
 
11
50
  ## [1.0.2](https://github.com/clarkedb/grift/releases/tag/v1.0.2) - 2021-11-11
12
51
 
@@ -14,7 +53,7 @@ This version fixes a bug that prevented the mocking of methods defined by a clas
14
53
 
15
54
  ### Fixed
16
55
 
17
- * Allow mocks of inherited methods
56
+ * Allow mocks of inherited methods ([#34](https://github.com/clarkedb/grift/pull/34))
18
57
 
19
58
  ## [1.0.1](https://github.com/clarkedb/grift/releases/tag/v1.0.1) - 2021-11-10
20
59
 
@@ -22,12 +61,7 @@ This version fixes a bug that prevented most mocking features in Grift from func
22
61
 
23
62
  ### Fixed
24
63
 
25
- * Uses relative path for yaml config files
26
-
27
- ### Updated
28
-
29
- * Updates `rubocop-performance`
30
- * Updates `rake`
64
+ * Uses relative path for yaml config files ([#28](https://github.com/clarkedb/grift/pull/28))
31
65
 
32
66
  ## [1.0.0](https://github.com/clarkedb/grift/releases/tag/v1.0.0) - 2021-11-06
33
67
 
@@ -35,12 +69,12 @@ The first major version of Grift! 100% documentation and 100% code coverage.
35
69
 
36
70
  ### Added
37
71
 
38
- * Spying on method
39
- * Mocking method return values
40
- * Mocking method implementation
41
- * Restricted methods that cannot be mocked
42
- * MiniTest Plugin to use hooks and clean up after tests
43
- * Documentation!
72
+ * Spying on method ([#9](https://github.com/clarkedb/grift/pull/9))
73
+ * Mocking method return values ([#9](https://github.com/clarkedb/grift/pull/9))
74
+ * Mocking method implementation ([#13](https://github.com/clarkedb/grift/pull/13))
75
+ * Restricted methods that cannot be mocked ([#20](https://github.com/clarkedb/grift/pull/20))
76
+ * MiniTest Plugin to use hooks and clean up after tests ([#17](https://github.com/clarkedb/grift/pull/17))
77
+ * Documentation! ([#23](https://github.com/clarkedb/grift/pull/23))
44
78
 
45
79
  ## [0.1.0](https://github.com/clarkedb/grift/releases/tag/v0.1.0) - 2021-10-12
46
80
 
data/Gemfile CHANGED
@@ -8,12 +8,14 @@ gemspec
8
8
  gem 'rake', '>= 12.0'
9
9
 
10
10
  group :development, :test do
11
+ gem 'codecov'
11
12
  gem 'minitest', '>= 5.0'
12
13
  gem 'minitest-reporters', '>= 1.4.3'
13
14
  gem 'simplecov', '>= 0.21.2'
14
15
  end
15
16
 
16
17
  group :development, :lint do
18
+ gem 'overcommit'
17
19
  gem 'rubocop'
18
20
  gem 'rubocop-minitest'
19
21
  gem 'rubocop-packaging', '>= 0.5'
data/Gemfile.lock CHANGED
@@ -1,7 +1,7 @@
1
1
  PATH
2
2
  remote: .
3
3
  specs:
4
- grift (1.0.2)
4
+ grift (2.0.1)
5
5
 
6
6
  GEM
7
7
  remote: https://rubygems.org/
@@ -9,36 +9,44 @@ GEM
9
9
  ansi (1.5.0)
10
10
  ast (2.4.2)
11
11
  builder (3.2.4)
12
+ childprocess (4.1.0)
13
+ codecov (0.6.0)
14
+ simplecov (>= 0.15, < 0.22)
12
15
  docile (1.4.0)
13
- minitest (5.14.4)
14
- minitest-reporters (1.4.3)
16
+ iniparse (1.5.0)
17
+ minitest (5.15.0)
18
+ minitest-reporters (1.5.0)
15
19
  ansi
16
20
  builder
17
21
  minitest (>= 5.0)
18
22
  ruby-progressbar
23
+ overcommit (0.58.0)
24
+ childprocess (>= 0.6.3, < 5)
25
+ iniparse (~> 1.4)
26
+ rexml (~> 3.2)
19
27
  parallel (1.21.0)
20
- parser (3.0.2.0)
28
+ parser (3.1.1.0)
21
29
  ast (~> 2.4.1)
22
- rainbow (3.0.0)
30
+ rainbow (3.1.1)
23
31
  rake (13.0.6)
24
- regexp_parser (2.1.1)
32
+ regexp_parser (2.2.1)
25
33
  rexml (3.2.5)
26
- rubocop (1.22.3)
34
+ rubocop (1.26.0)
27
35
  parallel (~> 1.10)
28
- parser (>= 3.0.0.0)
36
+ parser (>= 3.1.0.0)
29
37
  rainbow (>= 2.2.2, < 4.0)
30
38
  regexp_parser (>= 1.8, < 3.0)
31
39
  rexml
32
- rubocop-ast (>= 1.12.0, < 2.0)
40
+ rubocop-ast (>= 1.16.0, < 2.0)
33
41
  ruby-progressbar (~> 1.7)
34
42
  unicode-display_width (>= 1.4.0, < 3.0)
35
- rubocop-ast (1.12.0)
36
- parser (>= 3.0.1.1)
37
- rubocop-minitest (0.15.2)
43
+ rubocop-ast (1.16.0)
44
+ parser (>= 3.1.1.0)
45
+ rubocop-minitest (0.18.0)
38
46
  rubocop (>= 0.90, < 2.0)
39
47
  rubocop-packaging (0.5.1)
40
48
  rubocop (>= 0.89, < 2.0)
41
- rubocop-performance (1.12.0)
49
+ rubocop-performance (1.13.3)
42
50
  rubocop (>= 1.7.0, < 2.0)
43
51
  rubocop-ast (>= 0.4.0)
44
52
  ruby-progressbar (1.11.0)
@@ -54,9 +62,11 @@ PLATFORMS
54
62
  ruby
55
63
 
56
64
  DEPENDENCIES
65
+ codecov
57
66
  grift!
58
67
  minitest (>= 5.0)
59
68
  minitest-reporters (>= 1.4.3)
69
+ overcommit
60
70
  rake (>= 12.0)
61
71
  rubocop
62
72
  rubocop-minitest
@@ -65,4 +75,4 @@ DEPENDENCIES
65
75
  simplecov (>= 0.21.2)
66
76
 
67
77
  BUNDLED WITH
68
- 2.1.4
78
+ 2.2.32
data/README.md CHANGED
@@ -3,6 +3,7 @@
3
3
  [![gem version](https://badge.fury.io/rb/grift.svg)](https://rubygems.org/gems/grift)
4
4
  [![downloads](https://ruby-gem-downloads-badge.herokuapp.com/grift)](https://rubygems.org/gems/grift)
5
5
  [![build](https://github.com/clarkedb/grift/actions/workflows/ci.yml/badge.svg)](https://github.com/clarkedb/grift/actions?query=workflow%3ACI)
6
+ [![codecov](https://codecov.io/gh/clarkedb/grift/branch/main/graph/badge.svg)](https://codecov.io/gh/clarkedb/grift)
6
7
 
7
8
  Mocking and spying in Ruby's MiniTest framework
8
9
 
@@ -77,13 +78,23 @@ my_spy.mock_implementation do |arg1, arg2|
77
78
  end
78
79
  ```
79
80
 
81
+ or for a method taking keyword arguments:
82
+
83
+ ```ruby
84
+ my_spy = Grift.spy_on(MyClass, :my_method)
85
+ my_spy.mock_implementation do |arg1, arg2, **kwargs|
86
+ x = do_something(arg1, arg2, kwargs[:arg3], kwargs[:arg4])
87
+ do_something_else(x) # the last line will be returned
88
+ end
89
+ ```
90
+
80
91
  ### Chaining
81
92
 
82
93
  You can chain `mock_return_value` and `mock_implementation` after initializing the mock.
83
94
 
84
95
  ```ruby
85
- my_mock = Grift.spy_on(MyClass, :my_method).mock_implementation do |*args|
86
- do_something(*args)
96
+ my_mock = Grift.spy_on(MyClass, :my_method).mock_implementation do |*args, **kwargs|
97
+ do_something(*args, **kwargs)
87
98
  end
88
99
  #=> Grift::MockMethod object is returned
89
100
  ```
@@ -98,19 +109,31 @@ my_mock.mock.count
98
109
  #=> 2
99
110
 
100
111
  # get args for each call to the method while mocked
101
- my_mock.mock.calls
102
- #=> [['first_arg1', 'second_arg1'], ['first_arg2', 'second_arg2']]
112
+ my_mock.mock.calls[0].args
113
+ #=> ['first_arg1', 'second_arg1']
114
+
115
+ # get kwargs for each call to the method while mocked
116
+ my_mock.mock.calls[0].kwargs
117
+ #=> { first_arg1: 'value' }
103
118
 
104
119
  # get results (return value) for each call to the method while mocked
105
120
  my_mock.mock.results
106
121
  #=> ['result1', 'result2']
107
122
  ```
108
123
 
124
+ ## Requirements
125
+
126
+ Grift supports all Ruby versions >= 2.7 (including 3.1).
127
+
109
128
  ## Development
110
129
 
111
130
  After forking the repo, run `bin/setup` to install dependencies. Then, run `rake test` to run the tests. You can also run `bin/console` for an interactive prompt that will allow you to experiment.
112
131
 
113
- When developing, to install Grift whith your changes onto your local machine, run `bundle exec rake install` . To release a new version, update the version number in `version.rb` , and then run `bundle exec rake release` , which will create a git tag for the version, push git commits and tags, and push the `.gem` file to [rubygems.org](https://rubygems.org).
132
+ When developing, to install Grift whith your changes onto your local machine, run `bundle exec rake install` . For those with write access: to release a new version, update the version number in `version.rb` , and then run `bundle exec rake release` , which will create a git tag for the version, push git commits and tags, and push the `.gem` file to [rubygems.org](https://rubygems.org).
133
+
134
+ ### Docs
135
+
136
+ The docs are generated using YARD. To build the docs, first `gem install yard` . Then run `yardoc` to build the new docs. This is always done before a release to update the docs that get published and made available with the gem.
114
137
 
115
138
  ## Contributing
116
139
 
data/grift.gemspec CHANGED
@@ -12,20 +12,21 @@ Gem::Specification.new do |spec|
12
12
  spec.description = "A gem for simple mocking and spying in Ruby's MiniTest framework."
13
13
  spec.homepage = 'https://github.com/clarkedb/grift'
14
14
  spec.license = 'MIT'
15
- spec.required_ruby_version = Gem::Requirement.new('>= 2.5.0')
15
+ spec.required_ruby_version = Gem::Requirement.new('>= 2.7.0')
16
16
 
17
17
  spec.metadata = {
18
18
  'bug_tracker_uri' => "#{spec.homepage}/issues",
19
19
  'changelog_uri' => "#{spec.homepage}/blob/main/CHANGELOG.md",
20
20
  'documentation_uri' => spec.homepage.to_s,
21
21
  'homepage_uri' => spec.homepage.to_s,
22
+ 'rubygems_mfa_required' => 'true',
22
23
  'source_code_uri' => spec.homepage.to_s
23
24
  }
24
25
 
25
26
  # Specify which files should be added to the gem when it is released.
26
27
  # The `git ls-files -z` loads the files in the RubyGem that have been added into git.
27
28
  spec.files = Dir.chdir(File.expand_path(__dir__)) do
28
- `git ls-files -z`.split("\x0").reject { |f| f.match(%r{^(test|docs|spec|features)/}) }
29
+ `git ls-files -z`.split("\x0").reject { |f| f.match(%r{^(test|docs|spec|features)/}) } # rubocop:disable Packaging/GemspecGit
29
30
  end
30
31
  spec.bindir = 'exe'
31
32
  spec.executables = spec.files.grep(%r{^exe/}) { |f| File.basename(f) }
@@ -0,0 +1,166 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Grift
4
+ class MockMethod
5
+ class MockExecutions
6
+ ##
7
+ # An immutable Enumerable that stores the arguments used in a call for {Grift::MockMethod::MockExecutions}.
8
+ #
9
+ class MockArguments
10
+ include Enumerable
11
+
12
+ attr_reader :args, :kwargs
13
+
14
+ ##
15
+ # A new instance of MockArguments.
16
+ #
17
+ # @return [Grift::MockMethod::MockExecutions::MockArguments]
18
+ #
19
+ def initialize(args: [], kwargs: {})
20
+ @args = args.freeze
21
+ @kwargs = kwargs.freeze
22
+ end
23
+
24
+ ##
25
+ # Retrieves a stored argument by an accessor. For positional arguments this would be an integer
26
+ # corresponding to the arguments position in the method call signature. For keyword arguments
27
+ # this would be the key / parameter name as a symbol or string.
28
+ #
29
+ # @example
30
+ # my_mock = Grift.mock(Request, :new)
31
+ # request = Request.new('/users', method: 'GET')
32
+ # #=> <Request object>
33
+ # my_mock.mock.calls.last[0] # integer access for positional
34
+ # #=> '/users'
35
+ # my_mock.mock.calls.last[:method] # key access for keyword
36
+ # #=> 'GET'
37
+ #
38
+ # @param index [Integer, Symbol, String] the accessor for the desired argument
39
+ #
40
+ # @raise [Grift::Error] exception if accessor is not a supported type
41
+ #
42
+ # @return the specified value
43
+ #
44
+ def [](index)
45
+ if index.instance_of?(Integer)
46
+ @args[index]
47
+ elsif index.instance_of?(Symbol)
48
+ @kwargs[index]
49
+ elsif index.instance_of?(String)
50
+ @kwargs[index.to_sym]
51
+ else
52
+ raise(Grift::Error, "Cannot access by type '#{index.class}'. Expected an Integer, Symbol, or String")
53
+ end
54
+ end
55
+
56
+ ##
57
+ # Calls the given block once for each argument value, passing that value
58
+ # as a parameter.
59
+ #
60
+ # @return [void]
61
+ #
62
+ def each(&block)
63
+ @args.each(&block)
64
+ @kwargs.each_value(&block)
65
+ end
66
+
67
+ ##
68
+ # @see Enumerable#first
69
+ #
70
+ # Returns the last argument passed in the call.
71
+ #
72
+ # @example
73
+ # my_mock = Grift.mock(Request, :new)
74
+ # request = Request.new('/users', method: 'GET')
75
+ # #=> <Request object>
76
+ # my_mock.mock.calls.last.last
77
+ # #=> 'GET'
78
+ #
79
+ # @return [Any]
80
+ #
81
+ def last
82
+ @args.last || @kwargs.values.last
83
+ end
84
+
85
+ ##
86
+ # Returns the number of arguments passed in the call.
87
+ #
88
+ # @example
89
+ # arr = [1, 2, 3]
90
+ # my_mock = Grift.mock(Array, :count)
91
+ # arr.count
92
+ # #=> 3
93
+ # my_mock.mock.calls.last.length
94
+ # #=> 0
95
+ # arr.count(3)
96
+ # #=> 1
97
+ # my_mock.mock.calls.last.length
98
+ # #=> 1
99
+ #
100
+ # @return [Integer]
101
+ #
102
+ def length
103
+ @args.length + @kwargs.length
104
+ end
105
+
106
+ ##
107
+ # Returns true if the call included no arguments.
108
+ #
109
+ # @example
110
+ # arr = [1, 2, 3]
111
+ # my_mock = Grift.mock(Array, :count)
112
+ # arr.count
113
+ # #=> 3
114
+ # my_mock.mock.calls.last.empty?
115
+ # #=> true
116
+ # arr.count(2)
117
+ # #=> 1
118
+ # my_mock.mock.calls.last.empty?
119
+ # #=> false
120
+ #
121
+ # @return [Boolean] true if the no arguments were used
122
+ #
123
+ def empty?
124
+ @args.empty? && @kwargs.empty?
125
+ end
126
+
127
+ ##
128
+ # Returns the keyword parameter keys passed in the call.
129
+ #
130
+ # To get the values, use {Grift::MockMethod::MockExecutions::MockArguments#values}.
131
+ #
132
+ # @example
133
+ # my_mock = Grift.mock(Request, :new)
134
+ # request = Request.new('/users', method: 'GET')
135
+ # #=> <Request object>
136
+ # my_mock.mock.calls.last.keys
137
+ # #=> [:method]
138
+ #
139
+ # @return [Array<Symbol>] the parameter keys
140
+ #
141
+ def keys
142
+ @kwargs.keys
143
+ end
144
+
145
+ ##
146
+ # Returns the arguments passed in the call.
147
+ #
148
+ # If a keyword argument was used, then only the value will be returned.
149
+ # To get the keys, use {Grift::MockMethod::MockExecutions::MockArguments#keys}.
150
+ #
151
+ # @example
152
+ # my_mock = Grift.mock(Request, :new)
153
+ # request = Request.new('/users', method: 'GET')
154
+ # #=> <Request object>
155
+ # my_mock.mock.calls.last.keys
156
+ # #=> [:method]
157
+ #
158
+ # @return [Array] the argument values
159
+ #
160
+ def values
161
+ @args + @kwargs.values
162
+ end
163
+ end
164
+ end
165
+ end
166
+ end
@@ -9,7 +9,7 @@ module Grift
9
9
  ##
10
10
  # A new instance of MockExecutions.
11
11
  #
12
- # @return [Grift::MockMethod::MockExectuions]
12
+ # @return [Grift::MockMethod::MockExecutions]
13
13
  #
14
14
  def initialize
15
15
  @executions = []
@@ -21,14 +21,14 @@ module Grift
21
21
  # @example
22
22
  # my_mock = Grift.spy_on(Number, :+)
23
23
  # x = (3 + 4) + 5
24
- # my_mock.mock.calls
24
+ # my_mock.mock.calls.map(&:values)
25
25
  # #=> [[4], [5]]
26
26
  #
27
- # @return [Array<Array>] an array of arrays of args
27
+ # @return [Array<Grift::MockMethod::MockExecutions::MockArguments>] an array of MockArguments
28
28
  #
29
29
  def calls
30
30
  @executions.map do |exec|
31
- exec[:args]
31
+ exec[:arguments]
32
32
  end
33
33
  end
34
34
 
@@ -89,13 +89,17 @@ module Grift
89
89
  # Stores an args and result pair to the executions array.
90
90
  #
91
91
  # @example
92
- # mock_store = Grift::MockMethod::MockExecutions.new
93
- # mock_store.store([1, 1], [2])
92
+ # mock_executions = Grift::MockMethod::MockExecutions.new
93
+ # mock_executions.store(args: [1, 1], kwargs: { test: true }, result: 2)
94
94
  #
95
- # @return [Array] an array of results
95
+ # @param args [Array] the postitional args to store
96
+ # @param kwargs [Hash] the keyword args to store
97
+ # @param result the method result to store
98
+ #
99
+ # @return [Array] an updated array of executions
96
100
  #
97
- def store(args, result)
98
- @executions.push({ args: args, result: result })
101
+ def store(args: [], kwargs: {}, result: nil)
102
+ @executions.push({ arguments: MockArguments.new(args: args, kwargs: kwargs), result: result })
99
103
  end
100
104
  end
101
105
  end
@@ -6,7 +6,7 @@ module Grift
6
6
  # usually returns a {Grift::MockMethod}.
7
7
  #
8
8
  class MockMethod
9
- attr_reader :true_method_cached, :klass, :method_name
9
+ attr_reader :true_method_cached, :klass, :method_name, :method_access
10
10
 
11
11
  CACHE_METHOD_PREFIX = 'grift_cache'
12
12
  private_constant :CACHE_METHOD_PREFIX
@@ -39,13 +39,13 @@ module Grift
39
39
  @cache_method_name = "#{CACHE_METHOD_PREFIX}_#{method_name}".to_sym
40
40
 
41
41
  # class methods are really instance methods of the singleton class
42
- @class_method = klass.singleton_class.instance_methods(true).include?(method_name)
42
+ @class_method = klass.singleton_class.method_defined?(method_name, true) ||
43
+ klass.singleton_class.private_method_defined?(method_name, true)
43
44
 
44
- unless @class_method || class_instance.instance_methods(true).include?(method_name)
45
- raise(Grift::Error, "Cannot mock unknown method #{method_name} for class #{klass}")
46
- end
45
+ @method_access, @inherited = method_access_definition
46
+ raise(Grift::Error, "Cannot mock unknown method #{method_name} for class #{klass}") unless @method_access
47
47
 
48
- if class_instance.instance_methods.include?(@cache_method_name)
48
+ if class_instance.method_defined?(@cache_method_name)
49
49
  raise(Grift::Error, "Cannot mock already mocked method #{method_name} for class #{klass}")
50
50
  end
51
51
 
@@ -125,26 +125,29 @@ module Grift
125
125
  # #=> '7'
126
126
  #
127
127
  # @example
128
- # my_mock = Grift.spy_on(MyClass, :my_method).mock_implementation do |first, second|
129
- # [second, first]
128
+ # my_mock = Grift.spy_on(MyClass, :my_method).mock_implementation do |first, second, **kwargs|
129
+ # [second, kwargs[:third], first]
130
130
  # end
131
- # MyClass.my_method(1, 2)
132
- # #=> [2, 1]
131
+ # MyClass.my_method(1, 2, third: 3)
132
+ # #=> [2, 3, 1]
133
133
  #
134
- # @return [Grift::MockMethod] the mock itself
134
+ # @return [self] the mock itself
135
135
  #
136
- def mock_implementation(&block)
136
+ def mock_implementation(*)
137
+ raise(Grift::Error, 'Must provide a block for the new implementation') unless block_given?
138
+
137
139
  premock_setup
138
140
  mock_executions = @mock_executions # required to access inside class instance block
139
141
 
140
- class_instance.remove_method(@method_name) if class_instance.instance_methods(false).include?(@method_name)
141
- class_instance.define_method @method_name do |*args|
142
- return_value = block.call(*args)
142
+ class_instance.remove_method(@method_name) if !@inherited && method_defined?
143
+ class_instance.define_method @method_name do |*args, **kwargs|
144
+ return_value = yield(*args, **kwargs)
143
145
 
144
146
  # record the args passed in the call to the method and the result
145
- mock_executions.store(args, return_value)
147
+ mock_executions.store(args: args, result: return_value)
146
148
  return return_value
147
149
  end
150
+ class_instance.send(@method_access, @method_name)
148
151
 
149
152
  self
150
153
  end
@@ -162,18 +165,19 @@ module Grift
162
165
  #
163
166
  # @param return_value the value to return from the method
164
167
  #
165
- # @return [Grift::MockMethod] the mock itself
168
+ # @return [self] the mock itself
166
169
  #
167
170
  def mock_return_value(return_value = nil)
168
171
  premock_setup
169
172
  mock_executions = @mock_executions # required to access inside class instance block
170
173
 
171
- class_instance.remove_method(@method_name) if method_defined?
172
- class_instance.define_method @method_name do |*args|
174
+ class_instance.remove_method(@method_name) if !@inherited && method_defined?
175
+ class_instance.define_method @method_name do |*args, **kwargs|
173
176
  # record the args passed in the call to the method and the result
174
- mock_executions.store(args, return_value)
177
+ mock_executions.store(args: args, kwargs: kwargs, result: return_value)
175
178
  return return_value
176
179
  end
180
+ class_instance.send(@method_access, @method_name)
177
181
 
178
182
  self
179
183
  end
@@ -210,21 +214,22 @@ module Grift
210
214
  ##
211
215
  # Watches the method without mocking its impelementation or return value.
212
216
  #
213
- # @return [Grift::MockMethod] the mock itself
217
+ # @return [self] the mock itself
214
218
  #
215
219
  def watch_method
216
220
  premock_setup
217
221
  mock_executions = @mock_executions # required to access inside class instance block
218
222
  cache_method_name = @cache_method_name
219
223
 
220
- class_instance.remove_method(@method_name) if method_defined?
221
- class_instance.define_method @method_name do |*args|
222
- return_value = send(cache_method_name, *args)
224
+ class_instance.remove_method(@method_name) if !@inherited && method_defined?
225
+ class_instance.define_method @method_name do |*args, **kwargs, &block|
226
+ return_value = send(cache_method_name, *args, **kwargs, &block)
223
227
 
224
228
  # record the args passed in the call to the method and the result
225
- mock_executions.store(args, return_value)
229
+ mock_executions.store(args: args, kwargs: kwargs, result: return_value)
226
230
  return return_value
227
231
  end
232
+ class_instance.send(@method_access, @method_name)
228
233
 
229
234
  self
230
235
  end
@@ -238,7 +243,7 @@ module Grift
238
243
  raise(Grift::Error, 'Method is not cached') unless @true_method_cached
239
244
 
240
245
  class_instance.remove_method(@method_name) if method_defined?
241
- class_instance.alias_method(@method_name, @cache_method_name)
246
+ class_instance.alias_method(@method_name, @cache_method_name) unless @inherited
242
247
  class_instance.remove_method(@cache_method_name)
243
248
 
244
249
  @true_method_cached = false
@@ -289,7 +294,26 @@ module Grift
289
294
  # @return [Boolean]
290
295
  #
291
296
  def method_defined?
292
- class_instance.instance_methods(false).include?(@method_name)
297
+ class_instance.instance_methods(false).include?(@method_name) ||
298
+ class_instance.private_instance_methods(false).include?(@method_name)
299
+ end
300
+
301
+ ##
302
+ # Checks for the original access of the method (public, protected, private),
303
+ # and if that definition is inherited from an ancestor (super) class.
304
+ # Returns `nil` if no definition for the method is found.
305
+ #
306
+ # @return [Symbol] the method access
307
+ # @return [Boolean] true if the method is inherited
308
+ #
309
+ def method_access_definition
310
+ if class_instance.public_method_defined?(@method_name, true)
311
+ [:public, !class_instance.public_method_defined?(@method_name, false)]
312
+ elsif class_instance.protected_method_defined?(@method_name, true)
313
+ [:protected, !class_instance.protected_method_defined?(@method_name, false)]
314
+ elsif class_instance.private_method_defined?(@method_name, true)
315
+ [:private, !class_instance.private_method_defined?(@method_name, false)]
316
+ end
293
317
  end
294
318
  end
295
319
  end
data/lib/grift/version.rb CHANGED
@@ -2,6 +2,6 @@
2
2
 
3
3
  module Grift
4
4
  # gem version
5
- VERSION = '1.0.2'
5
+ VERSION = '2.0.1'
6
6
  public_constant :VERSION
7
7
  end
data/lib/grift.rb CHANGED
@@ -5,6 +5,7 @@ require 'grift/error'
5
5
  require 'grift/minitest_plugin'
6
6
  require 'grift/mock_method'
7
7
  require 'grift/mock_method/mock_executions'
8
+ require 'grift/mock_method/mock_executions/mock_arguments'
8
9
  require 'grift/mock_store'
9
10
  require 'grift/version'
10
11
 
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: 1.0.2
4
+ version: 2.0.1
5
5
  platform: ruby
6
6
  authors:
7
7
  - Clark Brown
8
8
  autorequire:
9
9
  bindir: exe
10
10
  cert_chain: []
11
- date: 2021-11-11 00:00:00.000000000 Z
11
+ date: 2022-03-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:
@@ -22,6 +22,7 @@ files:
22
22
  - ".github/dependabot.yml"
23
23
  - ".github/workflows/ci.yml"
24
24
  - ".gitignore"
25
+ - ".overcommit.yml"
25
26
  - ".rubocop.yml"
26
27
  - ".yardopts"
27
28
  - CHANGELOG.md
@@ -40,6 +41,7 @@ files:
40
41
  - lib/grift/minitest_plugin.rb
41
42
  - lib/grift/mock_method.rb
42
43
  - lib/grift/mock_method/mock_executions.rb
44
+ - lib/grift/mock_method/mock_executions/mock_arguments.rb
43
45
  - lib/grift/mock_store.rb
44
46
  - lib/grift/version.rb
45
47
  homepage: https://github.com/clarkedb/grift
@@ -50,6 +52,7 @@ metadata:
50
52
  changelog_uri: https://github.com/clarkedb/grift/blob/main/CHANGELOG.md
51
53
  documentation_uri: https://github.com/clarkedb/grift
52
54
  homepage_uri: https://github.com/clarkedb/grift
55
+ rubygems_mfa_required: 'true'
53
56
  source_code_uri: https://github.com/clarkedb/grift
54
57
  post_install_message:
55
58
  rdoc_options: []
@@ -59,14 +62,14 @@ required_ruby_version: !ruby/object:Gem::Requirement
59
62
  requirements:
60
63
  - - ">="
61
64
  - !ruby/object:Gem::Version
62
- version: 2.5.0
65
+ version: 2.7.0
63
66
  required_rubygems_version: !ruby/object:Gem::Requirement
64
67
  requirements:
65
68
  - - ">="
66
69
  - !ruby/object:Gem::Version
67
70
  version: '0'
68
71
  requirements: []
69
- rubygems_version: 3.1.2
72
+ rubygems_version: 3.2.32
70
73
  signing_key:
71
74
  specification_version: 4
72
75
  summary: Mocking and spying in MiniTest