grift 1.0.2 → 2.0.1
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/.github/dependabot.yml +3 -1
- data/.github/workflows/ci.yml +12 -2
- data/.overcommit.yml +8 -0
- data/.rubocop.yml +15 -0
- data/CHANGELOG.md +48 -14
- data/Gemfile +2 -0
- data/Gemfile.lock +24 -14
- data/README.md +28 -5
- data/grift.gemspec +3 -2
- data/lib/grift/mock_method/mock_executions/mock_arguments.rb +166 -0
- data/lib/grift/mock_method/mock_executions.rb +13 -9
- data/lib/grift/mock_method.rb +51 -27
- data/lib/grift/version.rb +1 -1
- data/lib/grift.rb +1 -0
- metadata +7 -4
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: '06800e91cb7e76be9232c7cf700d99ede4ba25212ed323abd1d5a4e944de455d'
|
4
|
+
data.tar.gz: 2d844fc80a5edcb9eb809c4946d7ee464465e8423b7a42e2f3a4ff4657e791d7
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: c3a2e6a02bab0921a5c91c48c5fbbf67d1a9dd0b0d50bd3c4d9dfb49d6f686623ca0c017d1d51f1df3220b4c1c83c1fd9a26070daa05c36c38d657a2476a4602
|
7
|
+
data.tar.gz: a7197e5fb4f7bbaf506426c9f4ed2a04733201f8a27a4f015df9af7466c9bd8563e700c2adaaef34211690c5ecf795ded15308701877bd5a0fbb6ed5278b366a
|
data/.github/dependabot.yml
CHANGED
data/.github/workflows/ci.yml
CHANGED
@@ -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: [
|
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: [
|
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
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
|
-
|
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 (
|
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
|
-
|
14
|
-
minitest
|
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.
|
28
|
+
parser (3.1.1.0)
|
21
29
|
ast (~> 2.4.1)
|
22
|
-
rainbow (3.
|
30
|
+
rainbow (3.1.1)
|
23
31
|
rake (13.0.6)
|
24
|
-
regexp_parser (2.
|
32
|
+
regexp_parser (2.2.1)
|
25
33
|
rexml (3.2.5)
|
26
|
-
rubocop (1.
|
34
|
+
rubocop (1.26.0)
|
27
35
|
parallel (~> 1.10)
|
28
|
-
parser (>= 3.
|
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.
|
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.
|
36
|
-
parser (>= 3.
|
37
|
-
rubocop-minitest (0.
|
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.
|
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.
|
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
|
-
#=> [
|
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` .
|
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.
|
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::
|
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<
|
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[:
|
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
|
-
#
|
93
|
-
#
|
92
|
+
# mock_executions = Grift::MockMethod::MockExecutions.new
|
93
|
+
# mock_executions.store(args: [1, 1], kwargs: { test: true }, result: 2)
|
94
94
|
#
|
95
|
-
# @
|
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
|
data/lib/grift/mock_method.rb
CHANGED
@@ -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.
|
42
|
+
@class_method = klass.singleton_class.method_defined?(method_name, true) ||
|
43
|
+
klass.singleton_class.private_method_defined?(method_name, true)
|
43
44
|
|
44
|
-
|
45
|
-
|
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.
|
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 [
|
134
|
+
# @return [self] the mock itself
|
135
135
|
#
|
136
|
-
def mock_implementation(
|
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
|
141
|
-
class_instance.define_method @method_name do |*args|
|
142
|
-
return_value =
|
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 [
|
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 [
|
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
data/lib/grift.rb
CHANGED
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:
|
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:
|
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.
|
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.
|
72
|
+
rubygems_version: 3.2.32
|
70
73
|
signing_key:
|
71
74
|
specification_version: 4
|
72
75
|
summary: Mocking and spying in MiniTest
|