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 +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
|
[![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
|
-
#=> [
|
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
|