grift 1.0.1 → 2.0.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: 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