grift 1.0.1 → 2.0.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: dfb5ff564a491305f0eccd4e3098fb3c132fca4b70fef6f07ff05fd5a087fe12
4
- data.tar.gz: bf327e20c122bc67ac490cce8409f80fb7e03fbeb81aca5e7443a2bad9b61c93
3
+ metadata.gz: 62cac26275a4af88278a38985c4edc2e3d001954fe68e491e242add325e49576
4
+ data.tar.gz: 03ddad7645f758c5708e353e8780001cfd4ce7b478cee2a4c5d60ae7a180cb80
5
5
  SHA512:
6
- metadata.gz: ebe31625bd0ecf33c2d5e0cfbadba80ac634cfaee932d0e54cb915c94720b538ab6748c56f311ece69177c078a8ce4394c39ae1b4c431bc78293d0c34b2e95d6
7
- data.tar.gz: 4779c67321958fe366269a293308c145e2402cb6a169a651a94e6ebdccd678b1b96154edd1170c5c5eeae84ddbe57f5ce769b41280dd9158a51f37beff679d0c
6
+ metadata.gz: 8df483db6759b17e6c8d7d6f05a81ed3b507b4be7d9a3d5b0f0adaa70bc8fec798d8d3e750d6f512a586e128ca0f492288d5b32a6e772a8ca6e2a67903116f41
7
+ data.tar.gz: c6460882f099ec2622fe92b99882e52b54df86c5aaf86311f86104326a94455235890ca5cc161448f8b7d5ab60d6853c1d65287764b8516eeebb5fd53ba3c8e3
@@ -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,20 +6,53 @@ 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
10
 
11
- ## [1.0.1](https://github.com/clarkedb/grift/releases/tag/v1.0.1) - 2021-11-10
11
+ ## [2.0.0](https://github.com/clarkedb/grift/releases/tag/v2.0.0) - 2022-03-14
12
12
 
13
- This version fixes a bug that prevented most mocking features in Grift from functioning as expected.
13
+ ### Changed
14
+
15
+ * Dropped support for Ruby 2.5 ([#69](https://github.com/clarkedb/grift/pull/69))
16
+ * Dropped support for Ruby 2.6 ([#72](https://github.com/clarkedb/grift/pull/72))
17
+ * 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))
18
+ + This changes the way that your tests will interact with mock calls
19
+ + When before `calls` returned an array, it returns a `Grift::MockMethod::MockExecutions::MockArguments` object
20
+ + Migrating to maintain previous behavior just requires appending `.args` to `calls`
21
+
22
+ ### Added
23
+
24
+ * Support for mocking private instance and class methods ([#68](https://github.com/clarkedb/grift/pull/68))
25
+ * Support for mocking methods that take positional and keyword arguments ([#72](https://github.com/clarkedb/grift/pull/72))
14
26
 
15
27
  ### Fixed
16
28
 
17
- * Uses relative path for yaml config files
29
+ * When mocking protected methods, the method now remains protected while mocked and after unmocking ([#68](https://github.com/clarkedb/grift/pull/68))
30
+ * When mocking inherited methods, the method goes back to the ancestor's definition after unmocking ([#69](https://github.com/clarkedb/grift/pull/69))
31
+ * When mocking methods with keyword arugments in Ruby 3.x, no error is thrown ([#72](https://github.com/clarkedb/grift/pull/72))
32
+
33
+ ## [1.1.0](https://github.com/clarkedb/grift/releases/tag/v1.1.0) - 2022-02-03
34
+
35
+ This version adds support for Ruby 3.1 and updates various dependencies.
36
+
37
+ ### Added
38
+
39
+ * Support Ruby 3.1 ([#52](https://github.com/clarkedb/grift/pull/52))
18
40
 
19
- ### Updated
41
+ ## [1.0.2](https://github.com/clarkedb/grift/releases/tag/v1.0.2) - 2021-11-11
42
+
43
+ This version fixes a bug that prevented the mocking of methods defined by a class's super class.
44
+
45
+ ### Fixed
46
+
47
+ * Allow mocks of inherited methods ([#34](https://github.com/clarkedb/grift/pull/34))
48
+
49
+ ## [1.0.1](https://github.com/clarkedb/grift/releases/tag/v1.0.1) - 2021-11-10
50
+
51
+ This version fixes a bug that prevented most mocking features in Grift from functioning as expected.
52
+
53
+ ### Fixed
20
54
 
21
- * Updates `rubocop-performance`
22
- * Updates `rake`
55
+ * Uses relative path for yaml config files ([#28](https://github.com/clarkedb/grift/pull/28))
23
56
 
24
57
  ## [1.0.0](https://github.com/clarkedb/grift/releases/tag/v1.0.0) - 2021-11-06
25
58
 
@@ -27,12 +60,12 @@ The first major version of Grift! 100% documentation and 100% code coverage.
27
60
 
28
61
  ### Added
29
62
 
30
- * Spying on method
31
- * Mocking method return values
32
- * Mocking method implementation
33
- * Restricted methods that cannot be mocked
34
- * MiniTest Plugin to use hooks and clean up after tests
35
- * Documentation!
63
+ * Spying on method ([#9](https://github.com/clarkedb/grift/pull/9))
64
+ * Mocking method return values ([#9](https://github.com/clarkedb/grift/pull/9))
65
+ * Mocking method implementation ([#13](https://github.com/clarkedb/grift/pull/13))
66
+ * Restricted methods that cannot be mocked ([#20](https://github.com/clarkedb/grift/pull/20))
67
+ * MiniTest Plugin to use hooks and clean up after tests ([#17](https://github.com/clarkedb/grift/pull/17))
68
+ * Documentation! ([#23](https://github.com/clarkedb/grift/pull/23))
36
69
 
37
70
  ## [0.1.0](https://github.com/clarkedb/grift/releases/tag/v0.1.0) - 2021-10-12
38
71
 
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.1)
4
+ grift (2.0.0)
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
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
 
@@ -36,6 +37,14 @@ class Minitest::Test
36
37
  end
37
38
  ```
38
39
 
40
+ Or for Ruby on Rails:
41
+
42
+ ```ruby
43
+ class ActiveSupport::TestCase
44
+ include Grift::MinitestPlugin
45
+ end
46
+ ```
47
+
39
48
  ## Usage
40
49
 
41
50
  For complete usage guide, see the [docs](https://clarkedb.github.io/grift/).
@@ -69,13 +78,22 @@ my_spy.mock_implementation do |arg1, arg2|
69
78
  end
70
79
  ```
71
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
+
72
90
  ### Chaining
73
91
 
74
92
  You can chain `mock_return_value` and `mock_implementation` after initializing the mock.
75
93
 
76
94
  ```ruby
77
- my_mock = Grift.spy_on(MyClass, :my_method).mock_implementation do |*args|
78
- do_something(*args)
95
+ my_mock = Grift.spy_on(MyClass, :my_method).mock_implementation do |*args, **kwargs|
96
+ do_something(*args, **kwargs)
79
97
  end
80
98
  #=> Grift::MockMethod object is returned
81
99
  ```
@@ -90,19 +108,31 @@ my_mock.mock.count
90
108
  #=> 2
91
109
 
92
110
  # get args for each call to the method while mocked
93
- my_mock.mock.calls
94
- #=> [['first_arg1', 'second_arg1'], ['first_arg2', 'second_arg2']]
111
+ my_mock.mock.calls[0].args
112
+ #=> ['first_arg1', 'second_arg1']
113
+
114
+ # get kwargs for each call to the method while mocked
115
+ my_mock.mock.calls[0].kwargs
116
+ #=> { first_arg1: 'value' }
95
117
 
96
118
  # get results (return value) for each call to the method while mocked
97
119
  my_mock.mock.results
98
120
  #=> ['result1', 'result2']
99
121
  ```
100
122
 
123
+ ## Requirements
124
+
125
+ Grift supports all Ruby versions >= 2.7 (including 3.1).
126
+
101
127
  ## Development
102
128
 
103
129
  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.
104
130
 
105
- 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).
131
+ 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).
132
+
133
+ ### Docs
134
+
135
+ 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.
106
136
 
107
137
  ## Contributing
108
138
 
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
@@ -29,7 +29,7 @@ module Grift
29
29
  #
30
30
  def initialize(klass, method_name, watch: true)
31
31
  if Grift.restricted_method?(klass, method_name)
32
- raise(Grift::Error, "Cannont mock restricted method #{method_name} for class #{klass}")
32
+ raise(Grift::Error, "Cannot mock restricted method #{method_name} for class #{klass}")
33
33
  end
34
34
 
35
35
  @klass = klass
@@ -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_instance.instance_methods(true).include?(method_name)
45
- raise(Grift::Error, "Cannont 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,27 @@ 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
137
  premock_setup
138
138
  mock_executions = @mock_executions # required to access inside class instance block
139
139
 
140
- class_instance.remove_method(@method_name)
141
- class_instance.define_method @method_name do |*args|
142
- return_value = block.call(*args)
140
+ class_instance.remove_method(@method_name) if !@inherited && method_defined?
141
+ class_instance.define_method @method_name do |*args, **kwargs|
142
+ return_value = yield(*args, **kwargs)
143
143
 
144
144
  # record the args passed in the call to the method and the result
145
- mock_executions.store(args, return_value)
145
+ mock_executions.store(args: args, result: return_value)
146
146
  return return_value
147
147
  end
148
+ class_instance.send(@method_access, @method_name)
148
149
 
149
150
  self
150
151
  end
@@ -162,18 +163,19 @@ module Grift
162
163
  #
163
164
  # @param return_value the value to return from the method
164
165
  #
165
- # @return [Grift::MockMethod] the mock itself
166
+ # @return [self] the mock itself
166
167
  #
167
168
  def mock_return_value(return_value = nil)
168
169
  premock_setup
169
170
  mock_executions = @mock_executions # required to access inside class instance block
170
171
 
171
- class_instance.remove_method(@method_name)
172
- class_instance.define_method @method_name do |*args|
172
+ class_instance.remove_method(@method_name) if !@inherited && method_defined?
173
+ class_instance.define_method @method_name do |*args, **kwargs|
173
174
  # record the args passed in the call to the method and the result
174
- mock_executions.store(args, return_value)
175
+ mock_executions.store(args: args, kwargs: kwargs, result: return_value)
175
176
  return return_value
176
177
  end
178
+ class_instance.send(@method_access, @method_name)
177
179
 
178
180
  self
179
181
  end
@@ -210,21 +212,22 @@ module Grift
210
212
  ##
211
213
  # Watches the method without mocking its impelementation or return value.
212
214
  #
213
- # @return [Grift::MockMethod] the mock itself
215
+ # @return [self] the mock itself
214
216
  #
215
217
  def watch_method
216
218
  premock_setup
217
219
  mock_executions = @mock_executions # required to access inside class instance block
218
220
  cache_method_name = @cache_method_name
219
221
 
220
- class_instance.remove_method(@method_name)
221
- class_instance.define_method @method_name do |*args|
222
- return_value = send(cache_method_name, *args)
222
+ class_instance.remove_method(@method_name) if !@inherited && method_defined?
223
+ class_instance.define_method @method_name do |*args, **kwargs|
224
+ return_value = send(cache_method_name, *args, **kwargs)
223
225
 
224
226
  # record the args passed in the call to the method and the result
225
- mock_executions.store(args, return_value)
227
+ mock_executions.store(args: args, kwargs: kwargs, result: return_value)
226
228
  return return_value
227
229
  end
230
+ class_instance.send(@method_access, @method_name)
228
231
 
229
232
  self
230
233
  end
@@ -237,8 +240,8 @@ module Grift
237
240
  def unmock_method
238
241
  raise(Grift::Error, 'Method is not cached') unless @true_method_cached
239
242
 
240
- class_instance.remove_method(@method_name)
241
- class_instance.alias_method(@method_name, @cache_method_name)
243
+ class_instance.remove_method(@method_name) if method_defined?
244
+ class_instance.alias_method(@method_name, @cache_method_name) unless @inherited
242
245
  class_instance.remove_method(@cache_method_name)
243
246
 
244
247
  @true_method_cached = false
@@ -280,5 +283,35 @@ module Grift
280
283
  def class_instance
281
284
  @class_method ? @klass.singleton_class : @klass
282
285
  end
286
+
287
+ ##
288
+ # Checks if the method is defined on the class instance. If the method is
289
+ # inherited from the super class and has not been mocked yet, this will
290
+ # return false because the super class defined the method.
291
+ #
292
+ # @return [Boolean]
293
+ #
294
+ def method_defined?
295
+ class_instance.instance_methods(false).include?(@method_name) ||
296
+ class_instance.private_instance_methods(false).include?(@method_name)
297
+ end
298
+
299
+ ##
300
+ # Checks for the original access of the method (public, protected, private),
301
+ # and if that definition is inherited from an ancestor (super) class.
302
+ # Returns `nil` if no definition for the method is found.
303
+ #
304
+ # @return [Symbol] the method access
305
+ # @return [Boolean] true if the method is inherited
306
+ #
307
+ def method_access_definition
308
+ if class_instance.public_method_defined?(@method_name, true)
309
+ [:public, !class_instance.public_method_defined?(@method_name, false)]
310
+ elsif class_instance.protected_method_defined?(@method_name, true)
311
+ [:protected, !class_instance.protected_method_defined?(@method_name, false)]
312
+ elsif class_instance.private_method_defined?(@method_name, true)
313
+ [:private, !class_instance.private_method_defined?(@method_name, false)]
314
+ end
315
+ end
283
316
  end
284
317
  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.1'
5
+ VERSION = '2.0.0'
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.1
4
+ version: 2.0.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: 2021-11-11 00:00:00.000000000 Z
11
+ date: 2022-03-15 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.1.6
70
73
  signing_key:
71
74
  specification_version: 4
72
75
  summary: Mocking and spying in MiniTest