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 +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 +46 -13
- data/Gemfile +2 -0
- data/Gemfile.lock +23 -13
- data/README.md +35 -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 +61 -28
- 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: 62cac26275a4af88278a38985c4edc2e3d001954fe68e491e242add325e49576
|
|
4
|
+
data.tar.gz: 03ddad7645f758c5708e353e8780001cfd4ce7b478cee2a4c5d60ae7a180cb80
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: 8df483db6759b17e6c8d7d6f05a81ed3b507b4be7d9a3d5b0f0adaa70bc8fec798d8d3e750d6f512a586e128ca0f492288d5b32a6e772a8ca6e2a67903116f41
|
|
7
|
+
data.tar.gz: c6460882f099ec2622fe92b99882e52b54df86c5aaf86311f86104326a94455235890ca5cc161448f8b7d5ab60d6853c1d65287764b8516eeebb5fd53ba3c8e3
|
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,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
|
-
|
|
9
|
+
None
|
|
10
10
|
|
|
11
|
-
## [
|
|
11
|
+
## [2.0.0](https://github.com/clarkedb/grift/releases/tag/v2.0.0) - 2022-03-14
|
|
12
12
|
|
|
13
|
-
|
|
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
|
-
*
|
|
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
|
-
|
|
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
|
-
*
|
|
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 (
|
|
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
|
-
|
|
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
|
data/README.md
CHANGED
|
@@ -3,6 +3,7 @@
|
|
|
3
3
|
[](https://rubygems.org/gems/grift)
|
|
4
4
|
[](https://rubygems.org/gems/grift)
|
|
5
5
|
[](https://github.com/clarkedb/grift/actions?query=workflow%3ACI)
|
|
6
|
+
[](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
|
-
#=> [
|
|
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` .
|
|
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.
|
|
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
|
|
@@ -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, "
|
|
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.
|
|
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,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 [
|
|
134
|
+
# @return [self] the mock itself
|
|
135
135
|
#
|
|
136
|
-
def mock_implementation(
|
|
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 =
|
|
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 [
|
|
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 [
|
|
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
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.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:
|
|
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.
|
|
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.
|
|
72
|
+
rubygems_version: 3.1.6
|
|
70
73
|
signing_key:
|
|
71
74
|
specification_version: 4
|
|
72
75
|
summary: Mocking and spying in MiniTest
|