crispy 0.2.0 → 0.3.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/.travis.yml +2 -3
- data/ChangeLog.md +11 -0
- data/LICENSE.txt +1 -1
- data/README.md +55 -16
- data/crispy.gemspec +1 -1
- data/lib/crispy/crispy_internal/class_spy.rb +17 -46
- data/lib/crispy/crispy_internal/double.rb +17 -9
- data/lib/crispy/crispy_internal/spy.rb +16 -29
- data/lib/crispy/crispy_internal/spy_base.rb +157 -0
- data/lib/crispy/crispy_received_message.rb +3 -3
- data/lib/crispy/version.rb +1 -1
- data/lib/crispy.rb +7 -3
- data/test/test_crispy.rb +150 -20
- metadata +6 -8
- data/lib/crispy/crispy_internal/spy_mixin.rb +0 -83
- data/lib/crispy/crispy_internal/stubber.rb +0 -31
- data/lib/crispy/crispy_internal/with_stubber.rb +0 -24
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 906a71958302bc1652930a6e9f1c5aa237eaa233
|
4
|
+
data.tar.gz: 27951041fba2e5b62fb3d78d3b26311455ac5863
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 569d2a0881f1f684974e70ed3e50c11d73038d1b07a29964521d6d50e6beaad11e3943e2b53455d9140707cc635f5b0e617fd4907608a919c59f990b833695c7
|
7
|
+
data.tar.gz: 4b8dee5f09c25f3494b22ce3cf9a2f511489fb8d1ef70d430abf8e6b9eae99ae1aadb0c2add730c6037bdc32cbf41032a09f19b42912eb247780d05a5e0ba334
|
data/.travis.yml
CHANGED
@@ -4,8 +4,8 @@ install:
|
|
4
4
|
- bundle
|
5
5
|
rvm:
|
6
6
|
- ruby-head
|
7
|
-
- 2.2.0
|
8
|
-
- 2.1.
|
7
|
+
- 2.2.0
|
8
|
+
- 2.1.5
|
9
9
|
- 2.0.0
|
10
10
|
script:
|
11
11
|
- CODECLIMATE_REPO_TOKEN=fe75ea4329c1e131ef79de66f8ba2f605fa2fd352bbbd61fed5024cb6eaaba73 bundle exec rubydoctest README.md
|
@@ -22,4 +22,3 @@ addons:
|
|
22
22
|
matrix:
|
23
23
|
allow_failures:
|
24
24
|
- rvm: ruby-head
|
25
|
-
- rvm: 2.2.0-preview1
|
data/ChangeLog.md
CHANGED
@@ -1,3 +1,14 @@
|
|
1
|
+
# 0.3.0 (2014.12.31)
|
2
|
+
|
3
|
+
- New Feature: Add stub feature to `ClassSpy`. [#23](https://github.com/igrep/crispy/pull/23)
|
4
|
+
- New Feature: Now double is automatically spied without `spy_into`. [#21](https://github.com/igrep/crispy/pull/21)
|
5
|
+
- Fix Bug: now `spy_into` replaces all stubber's methods with given stub spec when reinitialize a spy.[0f91579decbe27e6b05bec4b779dd1c3ede24380](https://github.com/igrep/crispy/commit/0f91579decbe27e6b05bec4b779dd1c3ede24380)
|
6
|
+
- Fix Bug: reset spy log when `spy_into`-ing an already spied object. e.g. Class.[#20](https://github.com/igrep/crispy/pull/20)
|
7
|
+
- New Feature: `spied?`. [#18](https://github.com/igrep/crispy/pull/18)
|
8
|
+
- Refactor many internal classes and tests. [#21](https://github.com/igrep/crispy/pull/21) [#22](https://github.com/igrep/crispy/pull/22) [#23](https://github.com/igrep/crispy/pull/23) [#24](https://github.com/igrep/crispy/pull/24)
|
9
|
+
- Loose required minitest's version.
|
10
|
+
- Minor document enhancements.
|
11
|
+
|
1
12
|
# 0.2.0 (2014.11.1)
|
2
13
|
|
3
14
|
- New Feature: `stub_const`. [#11](https://github.com/igrep/crispy/pull/11)
|
data/LICENSE.txt
CHANGED
data/README.md
CHANGED
@@ -9,7 +9,7 @@ Test Spy for Any Object in Ruby.
|
|
9
9
|
|
10
10
|
## Features
|
11
11
|
|
12
|
-
- Test spy for
|
12
|
+
- Test spy for **ANY** object by using `prepend` (Sorry, it runs by Ruby 2.0 or higher!)
|
13
13
|
- Extremely flexible query for received messages with `received_messages` method.
|
14
14
|
- By using Array and Enumerable's methods, you never have to remember the complex API and tons of the argument matchers in RSpec anymore!
|
15
15
|
- Makes mocks obsolete so you don't have to be worried about where to put the expectations (i.e. *before or after the subject method*).
|
@@ -42,8 +42,9 @@ doctest_require: './test/doctest-fixtures/your_cool_class.rb'
|
|
42
42
|
>> include Crispy # import spy, spy_into and any other functions from Crispy namespace.
|
43
43
|
|
44
44
|
>> object = YourCoolClass.new
|
45
|
-
>>
|
45
|
+
>> spy_into object # sneak into your object to spy.
|
46
46
|
|
47
|
+
# Use your object as usual.
|
47
48
|
>> object.your_cool_method 1, 2, 3
|
48
49
|
>> object.your_method_without_argument
|
49
50
|
>> object.your_lovely_method 'great', 'arguments'
|
@@ -101,7 +102,7 @@ Each argument is compared by `==` method.
|
|
101
102
|
#### Get more detailed log
|
102
103
|
|
103
104
|
You can check arbitrary received methods with the familliar Array's (and of course including Enumerable's!) methods such as `any?`, `all`, `first`, `[]`, `index`.
|
104
|
-
Because `spy(object).received_messages` returns an array of `CrispyReceivedMessage` instances.
|
105
|
+
Because `spy(object).received_messages` returns an array of `CrispyReceivedMessage` instances.
|
105
106
|
**You don't have to remember the tons of matchers for received arguments any more!!**
|
106
107
|
|
107
108
|
```ruby
|
@@ -118,6 +119,31 @@ Because `spy(object).received_messages` returns an array of `CrispyReceivedMessa
|
|
118
119
|
=> true
|
119
120
|
```
|
120
121
|
|
122
|
+
### Stub Methods of a Spy
|
123
|
+
|
124
|
+
```ruby
|
125
|
+
>> spy(object).stub(:your_cool_method, 'Awesome!')
|
126
|
+
>> object.your_cool_method
|
127
|
+
=> "Awesome!"
|
128
|
+
|
129
|
+
>> spy(object).stub(your_lovely_method: 'I love this method!', your_finalizer: 'Finalized!')
|
130
|
+
>> object.your_lovely_method
|
131
|
+
=> "I love this method!"
|
132
|
+
>> object.your_finalizer
|
133
|
+
=> "Finalized!"
|
134
|
+
```
|
135
|
+
|
136
|
+
Of cource stubbed methods are spied as well.
|
137
|
+
|
138
|
+
```ruby
|
139
|
+
>> spy(object).received? :your_cool_method
|
140
|
+
=> true
|
141
|
+
|
142
|
+
# `spy(object)` keeps its spied log of a method even after stubbing the method.
|
143
|
+
>> spy(object).count_received :your_lovely_method
|
144
|
+
=> 3
|
145
|
+
```
|
146
|
+
|
121
147
|
### Spy on Instances of a Class
|
122
148
|
|
123
149
|
```ruby
|
@@ -161,7 +187,7 @@ In addition, you can check which instance calles a method as well as its argumen
|
|
161
187
|
=> true
|
162
188
|
```
|
163
189
|
|
164
|
-
Note that `spy_of_instances` stops
|
190
|
+
Note that `spy_of_instances` stops spying after called methods with `with_receiver` (or `with_receiver?`) prefix.
|
165
191
|
This is to prevent the spy from unexpectedly logging methods used to compare its receiver (such as `==`).
|
166
192
|
|
167
193
|
```ruby
|
@@ -175,35 +201,36 @@ This is to prevent the spy from unexpectedly logging methods used to compare its
|
|
175
201
|
>> # Perhaps you don't want to log methods in test code.
|
176
202
|
>> instance.some_method_for_testing
|
177
203
|
>> spy_of_instances(YourCoolClass::Again).received? :some_method_for_testing
|
178
|
-
|
204
|
+
=> false
|
179
205
|
```
|
180
206
|
|
181
207
|
If you want to restart spying, use `restart` method literally.
|
182
208
|
|
183
209
|
```ruby
|
184
|
-
>>
|
210
|
+
>> spy_of_instances(YourCoolClass::Again).restart
|
185
211
|
|
186
212
|
>> instance.some_method_for_testing
|
187
213
|
>> spy_of_instances(YourCoolClass::Again).received? :some_method_for_testing
|
188
|
-
|
214
|
+
=> true
|
189
215
|
```
|
190
216
|
|
191
|
-
### Stub Methods of a
|
217
|
+
### Stub Methods of Instances of a Class
|
192
218
|
|
193
219
|
```ruby
|
194
|
-
>>
|
195
|
-
>> object.your_cool_method
|
196
|
-
=> "Awesome!"
|
220
|
+
>> spy_of_instances(YourCoolClass).stub(your_lovely_method: 'Even more lovely!', your_cool_method: 'much cooler!')
|
197
221
|
|
198
|
-
>>
|
199
|
-
|
200
|
-
|
201
|
-
|
202
|
-
=> "Finalized!"
|
222
|
+
>> instance1.your_lovely_method
|
223
|
+
=> "Even more lovely!"
|
224
|
+
>> instance2.your_cool_method
|
225
|
+
=> "much cooler!"
|
203
226
|
```
|
204
227
|
|
205
228
|
### Stub Methods of a Double
|
206
229
|
|
230
|
+
Double can call Spy's method directly.
|
231
|
+
You do NOT need to write code such as `spy(your_double).stub(...)`.
|
232
|
+
Just `your_double.stub(...)`.
|
233
|
+
|
207
234
|
```ruby
|
208
235
|
>> your_awesome_double = double('your awesome double', nice!: '+1!', sexy?: true)
|
209
236
|
>> your_awesome_double.nice!
|
@@ -216,6 +243,18 @@ If you want to restart spying, use `restart` method literally.
|
|
216
243
|
=> "can be stubbed."
|
217
244
|
```
|
218
245
|
|
246
|
+
### Spy on a Double
|
247
|
+
|
248
|
+
A double is spied without `spy_into`-ing.
|
249
|
+
And as `double.stub(...)`, Double can also call Spy's method such as `received?`
|
250
|
+
|
251
|
+
```ruby
|
252
|
+
>> your_awesome_double.received? :nice!
|
253
|
+
=> true
|
254
|
+
>> your_awesome_double.count_received :another_method
|
255
|
+
=> 1
|
256
|
+
```
|
257
|
+
|
219
258
|
### Stub Constants
|
220
259
|
|
221
260
|
Specify the **fully qualified name of the constant** instead of the constant itself.
|
data/crispy.gemspec
CHANGED
@@ -22,7 +22,7 @@ Gem::Specification.new do |spec|
|
|
22
22
|
|
23
23
|
spec.add_development_dependency "bundler", "~> 1.7"
|
24
24
|
spec.add_development_dependency "rake"
|
25
|
-
spec.add_development_dependency "minitest", "~> 5
|
25
|
+
spec.add_development_dependency "minitest", "~> 5"
|
26
26
|
spec.add_development_dependency "pry"
|
27
27
|
spec.add_development_dependency "byebug"
|
28
28
|
spec.add_development_dependency "rubydoctest"
|
@@ -1,29 +1,26 @@
|
|
1
1
|
require 'crispy/crispy_received_message_with_receiver'
|
2
|
-
require 'crispy/crispy_internal/
|
3
|
-
require 'crispy/crispy_internal/with_stubber'
|
2
|
+
require 'crispy/crispy_internal/spy_base'
|
4
3
|
|
5
4
|
module Crispy
|
6
5
|
module CrispyInternal
|
7
|
-
class ClassSpy <
|
8
|
-
include SpyMixin
|
9
|
-
#include WithStubber
|
6
|
+
class ClassSpy < SpyBase
|
10
7
|
|
11
8
|
@registry = {}
|
12
9
|
|
13
|
-
def initialize klass
|
14
|
-
spy = self
|
15
|
-
super() do
|
16
|
-
define_method(:__CRISPY_CLASS_SPY__) { spy }
|
17
|
-
end
|
18
|
-
|
10
|
+
def initialize klass, stubs_map = {}
|
19
11
|
@received_messages_with_receiver = []
|
20
|
-
initialize_spy
|
21
12
|
|
22
|
-
|
23
|
-
#prepend_stubber klass
|
13
|
+
super
|
24
14
|
|
25
|
-
|
26
|
-
|
15
|
+
self.class.register spy: self, of_class: klass
|
16
|
+
end
|
17
|
+
|
18
|
+
def self.of_target klass
|
19
|
+
@registry[klass]
|
20
|
+
end
|
21
|
+
|
22
|
+
def target_to_class target_class
|
23
|
+
target_class
|
27
24
|
end
|
28
25
|
|
29
26
|
def received_messages
|
@@ -63,42 +60,16 @@ module Crispy
|
|
63
60
|
@received_messages_with_receiver.clear
|
64
61
|
end
|
65
62
|
|
66
|
-
def
|
67
|
-
|
68
|
-
|
69
|
-
::Crispy::CrispyReceivedMessageWithReceiver.new(receiver, method_name, *arguments, &attached_block)
|
70
|
-
end
|
71
|
-
end
|
72
|
-
|
73
|
-
def define_wrapper method_name
|
74
|
-
define_method method_name do|*arguments, &attached_block|
|
75
|
-
__CRISPY_CLASS_SPY__.append_received_message_with_receiver self, method_name, *arguments, &attached_block
|
76
|
-
|
77
|
-
super(*arguments, &attached_block)
|
78
|
-
end
|
79
|
-
method_name
|
80
|
-
end
|
81
|
-
private :define_wrapper
|
82
|
-
|
83
|
-
def self.new klass#, stubs_map = {}
|
84
|
-
spy = self.of_class(klass)
|
85
|
-
if spy
|
86
|
-
spy.restart
|
87
|
-
spy.erase_log
|
88
|
-
spy
|
89
|
-
else
|
90
|
-
super
|
91
|
-
end
|
63
|
+
def append_received_message receiver, method_name, *arguments, &attached_block
|
64
|
+
@received_messages_with_receiver <<
|
65
|
+
::Crispy::CrispyReceivedMessageWithReceiver.new(receiver, method_name, *arguments, &attached_block)
|
92
66
|
end
|
67
|
+
private :append_received_message
|
93
68
|
|
94
69
|
def self.register(spy: nil, of_class: nil)
|
95
70
|
@registry[of_class] = spy
|
96
71
|
end
|
97
72
|
|
98
|
-
def self.of_class(klass)
|
99
|
-
@registry[klass]
|
100
|
-
end
|
101
|
-
|
102
73
|
end
|
103
74
|
end
|
104
75
|
end
|
@@ -1,23 +1,31 @@
|
|
1
|
-
require 'crispy/crispy_internal/with_stubber'
|
2
|
-
|
3
1
|
module Crispy
|
4
2
|
module CrispyInternal
|
5
3
|
class Double
|
6
|
-
include WithStubber
|
7
4
|
|
8
5
|
def initialize name_or_stubs_map = nil, stubs_map = {}
|
9
6
|
if name_or_stubs_map.is_a? ::Hash
|
10
7
|
@name = ''.freeze
|
11
|
-
|
8
|
+
@spy = ::Crispy.spy_into(self, name_or_stubs_map)
|
12
9
|
else
|
13
10
|
@name = name_or_stubs_map
|
14
|
-
|
11
|
+
@spy = ::Crispy.spy_into(self, stubs_map)
|
15
12
|
end
|
16
|
-
|
17
|
-
|
18
|
-
|
13
|
+
end
|
14
|
+
|
15
|
+
def stub *arguments, &definition
|
16
|
+
@spy.stub(*arguments, &definition)
|
17
|
+
end
|
18
|
+
|
19
|
+
def received_messages
|
20
|
+
@spy.received_messages
|
21
|
+
end
|
22
|
+
|
23
|
+
SpyBase::COMMON_RECEIVED_MESSAGE_METHODS_DEFINITION.each_key do|method_name|
|
24
|
+
binding.eval(<<-END, __FILE__, (__LINE__ + 1))
|
25
|
+
def #{method_name} received_method_name, *received_arguments, &received_block
|
26
|
+
@spy.#{method_name} received_method_name, *received_arguments, &received_block
|
19
27
|
end
|
20
|
-
|
28
|
+
END
|
21
29
|
end
|
22
30
|
|
23
31
|
end
|
@@ -1,54 +1,41 @@
|
|
1
1
|
require 'crispy/crispy_received_message'
|
2
|
-
require 'crispy/crispy_internal/
|
3
|
-
require 'crispy/crispy_internal/with_stubber'
|
2
|
+
require 'crispy/crispy_internal/spy_base'
|
4
3
|
|
5
4
|
module Crispy
|
6
5
|
module CrispyInternal
|
7
|
-
class Spy <
|
8
|
-
include SpyMixin
|
9
|
-
include WithStubber
|
6
|
+
class Spy < SpyBase
|
10
7
|
|
11
8
|
attr_reader :received_messages
|
12
9
|
|
13
10
|
def initialize target, stubs_map = {}
|
14
|
-
|
15
|
-
|
11
|
+
spy = self
|
12
|
+
module_eval do
|
16
13
|
define_method(:__CRISPY_SPY__) { spy }
|
17
14
|
end
|
18
15
|
|
19
16
|
@received_messages = []
|
20
|
-
|
21
|
-
|
22
|
-
singleton_class =
|
23
|
-
class << target
|
24
|
-
self
|
25
|
-
end
|
26
|
-
initialize_stubber stubs_map
|
27
|
-
prepend_stubber singleton_class
|
17
|
+
super
|
18
|
+
end
|
28
19
|
|
29
|
-
|
20
|
+
def target_to_class target
|
21
|
+
class << target
|
22
|
+
self
|
23
|
+
end
|
30
24
|
end
|
31
25
|
|
32
26
|
def erase_log
|
33
27
|
@received_messages.clear
|
34
28
|
end
|
35
29
|
|
36
|
-
def
|
37
|
-
|
38
|
-
@received_messages <<
|
39
|
-
::Crispy::CrispyReceivedMessage.new(method_name, *arguments, &attached_block)
|
40
|
-
end
|
30
|
+
def self.of_target target
|
31
|
+
(defined? target.__CRISPY_SPY__) && target.__CRISPY_SPY__
|
41
32
|
end
|
42
33
|
|
43
|
-
def
|
44
|
-
|
45
|
-
|
46
|
-
|
47
|
-
super(*arguments, &attached_block)
|
48
|
-
end
|
49
|
-
method_name
|
34
|
+
def append_received_message receiver, method_name, *arguments, &attached_block
|
35
|
+
@received_messages <<
|
36
|
+
::Crispy::CrispyReceivedMessage.new(method_name, *arguments, &attached_block)
|
50
37
|
end
|
51
|
-
private :
|
38
|
+
private :append_received_message
|
52
39
|
|
53
40
|
end
|
54
41
|
end
|
@@ -0,0 +1,157 @@
|
|
1
|
+
require 'crispy/crispy_received_message'
|
2
|
+
|
3
|
+
module Crispy
|
4
|
+
module CrispyInternal
|
5
|
+
class SpyBase < ::Module
|
6
|
+
|
7
|
+
public :remove_method
|
8
|
+
|
9
|
+
BLACK_LISTED_METHODS = [
|
10
|
+
:__CRISPY_SPY__,
|
11
|
+
]
|
12
|
+
|
13
|
+
def initialize target, stubs_map = {}
|
14
|
+
prepend_features target_to_class(target)
|
15
|
+
|
16
|
+
@stubbed_methods = []
|
17
|
+
stub stubs_map
|
18
|
+
|
19
|
+
@spying = true
|
20
|
+
end
|
21
|
+
|
22
|
+
def self.new target, stubs_map = {}
|
23
|
+
spy = self.of_target(target)
|
24
|
+
if spy
|
25
|
+
spy.restart
|
26
|
+
spy.erase_log
|
27
|
+
spy.reinitialize_stubber stubs_map
|
28
|
+
spy
|
29
|
+
else
|
30
|
+
super
|
31
|
+
end
|
32
|
+
end
|
33
|
+
|
34
|
+
def self.of_target target
|
35
|
+
raise NotImplementedError
|
36
|
+
end
|
37
|
+
|
38
|
+
def target_to_class target
|
39
|
+
raise NotImplementedError
|
40
|
+
end
|
41
|
+
|
42
|
+
def received_messages
|
43
|
+
raise NotImplementedError
|
44
|
+
end
|
45
|
+
|
46
|
+
def erase_log
|
47
|
+
raise NotImplementedError
|
48
|
+
end
|
49
|
+
|
50
|
+
def append_received_message receiver, method_name, *arguments, &attached_block
|
51
|
+
raise NotImplementedError
|
52
|
+
end
|
53
|
+
|
54
|
+
def stop
|
55
|
+
@spying = false
|
56
|
+
end
|
57
|
+
|
58
|
+
def restart
|
59
|
+
@spying = true
|
60
|
+
end
|
61
|
+
|
62
|
+
def define_wrapper method_name
|
63
|
+
spy = self
|
64
|
+
define_method method_name do|*arguments, &attached_block|
|
65
|
+
spy.append_received_message_when_spying(self, method_name, *arguments, &attached_block)
|
66
|
+
|
67
|
+
super(*arguments, &attached_block)
|
68
|
+
end
|
69
|
+
method_name
|
70
|
+
end
|
71
|
+
private :define_wrapper
|
72
|
+
|
73
|
+
def append_received_message_when_spying receiver, method_name, *arguments, &attached_block
|
74
|
+
if @spying
|
75
|
+
append_received_message receiver, method_name, *arguments, &attached_block
|
76
|
+
end
|
77
|
+
end
|
78
|
+
|
79
|
+
COMMON_RECEIVED_MESSAGE_METHODS_DEFINITION = {
|
80
|
+
'received?' => 'include? %s',
|
81
|
+
'received_once?' => 'one? {|self_thing| self_thing == %s }',
|
82
|
+
'count_received' => 'count %s',
|
83
|
+
}
|
84
|
+
|
85
|
+
COMMON_RECEIVED_MESSAGE_METHODS_DEFINITION.each do|method_name, core_definition|
|
86
|
+
binding.eval(<<-END, __FILE__, (__LINE__ + 1))
|
87
|
+
def #{method_name} received_method_name, *received_arguments, &received_block
|
88
|
+
assert_symbol! received_method_name
|
89
|
+
if received_arguments.empty? and received_block.nil?
|
90
|
+
received_messages.map(&:method_name).#{sprintf(core_definition, 'received_method_name')}
|
91
|
+
else
|
92
|
+
received_message = ::Crispy::CrispyReceivedMessage.new(
|
93
|
+
received_method_name, *received_arguments, &received_block
|
94
|
+
)
|
95
|
+
received_messages.#{sprintf(core_definition, 'received_message')}
|
96
|
+
end
|
97
|
+
end
|
98
|
+
END
|
99
|
+
end
|
100
|
+
|
101
|
+
def stub method_name_or_hash, returned_value = nil, &definition
|
102
|
+
case method_name_or_hash
|
103
|
+
when Hash
|
104
|
+
hash = method_name_or_hash
|
105
|
+
hash.each do|method_name, value|
|
106
|
+
stub method_name, value
|
107
|
+
end
|
108
|
+
when ::Symbol, ::String
|
109
|
+
@stubbed_methods << method_name_or_hash
|
110
|
+
|
111
|
+
self.module_exec method_name_or_hash do|method_name|
|
112
|
+
spy = self
|
113
|
+
|
114
|
+
# remove methods already defined (maybe by define_wrapper) to avoid warning.
|
115
|
+
remove_method method_name if public_method_defined? method_name
|
116
|
+
|
117
|
+
# TODO: should not ignore arguments?
|
118
|
+
define_method(method_name) do|*arguments, &block|
|
119
|
+
spy.append_received_message_when_spying self, method_name, *arguments, &block
|
120
|
+
returned_value
|
121
|
+
end
|
122
|
+
end
|
123
|
+
end
|
124
|
+
self
|
125
|
+
end
|
126
|
+
|
127
|
+
def reinitialize_stubber stubs_map = {}
|
128
|
+
remove_method(*@stubbed_methods)
|
129
|
+
@stubbed_methods.clear
|
130
|
+
stub stubs_map
|
131
|
+
end
|
132
|
+
|
133
|
+
def prepend_features klass
|
134
|
+
super
|
135
|
+
|
136
|
+
klass.public_instance_methods.each do|method_name|
|
137
|
+
next if method_name == :__CRISPY_SPY__
|
138
|
+
self.module_eval { define_wrapper(method_name) }
|
139
|
+
end
|
140
|
+
klass.protected_instance_methods.each do|method_name|
|
141
|
+
self.module_eval { protected define_wrapper(method_name) }
|
142
|
+
end
|
143
|
+
klass.private_instance_methods.each do|method_name|
|
144
|
+
self.module_eval { private define_wrapper(method_name) }
|
145
|
+
end
|
146
|
+
end
|
147
|
+
private :prepend_features
|
148
|
+
|
149
|
+
def assert_symbol! maybe_symbol
|
150
|
+
unless maybe_symbol.respond_to?(:to_sym) && maybe_symbol.to_sym.instance_of?(::Symbol)
|
151
|
+
raise TypeError, "TypeError: no implicit conversion from #{maybe_symbol.inspect} to symbol"
|
152
|
+
end
|
153
|
+
end
|
154
|
+
|
155
|
+
end
|
156
|
+
end
|
157
|
+
end
|
@@ -17,14 +17,14 @@ module Crispy
|
|
17
17
|
|
18
18
|
def == other
|
19
19
|
@method_name == other.method_name &&
|
20
|
-
@arguments == other.arguments &&
|
21
|
-
@attached_block == other.attached_block
|
20
|
+
@arguments == other.arguments # &&
|
21
|
+
# @attached_block == other.attached_block
|
22
22
|
end
|
23
23
|
|
24
24
|
CLASS_NAME = self.name
|
25
25
|
|
26
26
|
def to_s
|
27
|
-
"#<#{CLASS_NAME}[#{
|
27
|
+
"#<#{CLASS_NAME}[#{@method_name.inspect}, *#{@arguments.inspect}]>"
|
28
28
|
end
|
29
29
|
|
30
30
|
alias inspect to_s
|
data/lib/crispy/version.rb
CHANGED
data/lib/crispy.rb
CHANGED
@@ -18,8 +18,8 @@ module Crispy
|
|
18
18
|
end
|
19
19
|
|
20
20
|
# Make and returns a Crispy::ClassSpy's instance to spy all instances of a class.
|
21
|
-
def spy_into_instances klass
|
22
|
-
::Crispy::CrispyInternal::ClassSpy.new klass
|
21
|
+
def spy_into_instances klass, stubs_map = {}
|
22
|
+
::Crispy::CrispyInternal::ClassSpy.new klass, stubs_map
|
23
23
|
end
|
24
24
|
|
25
25
|
def spy object
|
@@ -27,7 +27,7 @@ module Crispy
|
|
27
27
|
end
|
28
28
|
|
29
29
|
def spy_of_instances klass
|
30
|
-
::Crispy::CrispyInternal::ClassSpy.
|
30
|
+
::Crispy::CrispyInternal::ClassSpy.of_target(klass)
|
31
31
|
end
|
32
32
|
|
33
33
|
def stub_const full_const_name, value
|
@@ -35,4 +35,8 @@ module Crispy
|
|
35
35
|
::Crispy::CrispyInternal::ConstChanger.save full_const_name, saved_value
|
36
36
|
end
|
37
37
|
|
38
|
+
def spied? object
|
39
|
+
defined? object.__CRISPY_SPY__
|
40
|
+
end
|
41
|
+
|
38
42
|
end
|
data/test/test_crispy.rb
CHANGED
@@ -7,6 +7,20 @@ require 'minitest/autorun'
|
|
7
7
|
class TestCrispy < MiniTest::Test
|
8
8
|
include ::Crispy
|
9
9
|
|
10
|
+
module CommonSpyTests
|
11
|
+
|
12
|
+
def test_spy_is_also_returned_by_spy_into_method
|
13
|
+
assert_same @subject, @returned_spy
|
14
|
+
end
|
15
|
+
|
16
|
+
def test_spy_raises_error_given_non_symbol_as_method_name
|
17
|
+
assert_raises(::TypeError){ @subject.received?(nil) }
|
18
|
+
assert_raises(::TypeError){ @subject.received_once?(nil) }
|
19
|
+
assert_raises(::TypeError){ @subject.count_received(nil) }
|
20
|
+
end
|
21
|
+
|
22
|
+
end
|
23
|
+
|
10
24
|
# Inherit BasicObject because it has fewer meta-programming methods than Object
|
11
25
|
class ObjectClass < BasicObject
|
12
26
|
|
@@ -25,10 +39,13 @@ class TestCrispy < MiniTest::Test
|
|
25
39
|
def baz
|
26
40
|
end
|
27
41
|
def method_to_stub1
|
28
|
-
|
42
|
+
'method to stub 1 (before stubbed)'
|
29
43
|
end
|
30
44
|
def method_to_stub2
|
31
|
-
|
45
|
+
'method to stub 2 (before stubbed)'
|
46
|
+
end
|
47
|
+
def method_to_stub3
|
48
|
+
'method to stub 3 (before stubbed)'
|
32
49
|
end
|
33
50
|
|
34
51
|
def private_foo a
|
@@ -36,6 +53,43 @@ class TestCrispy < MiniTest::Test
|
|
36
53
|
end
|
37
54
|
private :private_foo
|
38
55
|
|
56
|
+
def self.hoge a, b, c
|
57
|
+
private_foo a
|
58
|
+
[a, b, c]
|
59
|
+
end
|
60
|
+
def self.foo
|
61
|
+
123
|
62
|
+
end
|
63
|
+
|
64
|
+
def self.stubbed_method1
|
65
|
+
'before stubbed 1'
|
66
|
+
end
|
67
|
+
def self.stubbed_method2
|
68
|
+
'before stubbed 2'
|
69
|
+
end
|
70
|
+
def self.stubbed_method3
|
71
|
+
'before stubbed 3'
|
72
|
+
end
|
73
|
+
|
74
|
+
def self.private_foo a
|
75
|
+
:private_foo
|
76
|
+
end
|
77
|
+
private_class_method :private_foo
|
78
|
+
|
79
|
+
end
|
80
|
+
|
81
|
+
class TestCrispySpied < TestCrispy
|
82
|
+
|
83
|
+
def test_spied_object_is_spied
|
84
|
+
spied_object = Object.new
|
85
|
+
spy_into spied_object
|
86
|
+
assert spied?(spied_object)
|
87
|
+
end
|
88
|
+
|
89
|
+
def test_non_spied_object_is_not_spied
|
90
|
+
assert not(spied?(Object.new))
|
91
|
+
end
|
92
|
+
|
39
93
|
end
|
40
94
|
|
41
95
|
class TestCrispyStubConst < TestCrispy
|
@@ -58,6 +112,7 @@ class TestCrispy < MiniTest::Test
|
|
58
112
|
end
|
59
113
|
|
60
114
|
class TestCrispySpyInto < TestCrispy
|
115
|
+
include CommonSpyTests
|
61
116
|
|
62
117
|
def setup
|
63
118
|
@object = ObjectClass.new
|
@@ -134,14 +189,41 @@ class TestCrispy < MiniTest::Test
|
|
134
189
|
assert_equal(:stubbed2, @object.method_to_stub2)
|
135
190
|
end
|
136
191
|
|
137
|
-
|
138
|
-
|
192
|
+
end
|
193
|
+
|
194
|
+
class TestCrispySpyIntoClass < TestCrispy
|
195
|
+
|
196
|
+
def setup
|
197
|
+
spy_into ObjectClass, stubbed_method1: 1, stubbed_method2: 2
|
198
|
+
|
199
|
+
ObjectClass.hoge 1, 2, 3
|
200
|
+
ObjectClass.foo
|
201
|
+
ObjectClass.hoge 3, 4, 5
|
202
|
+
|
203
|
+
@subject = spy(ObjectClass)
|
139
204
|
end
|
140
205
|
|
141
|
-
def
|
142
|
-
|
143
|
-
|
144
|
-
|
206
|
+
def test_spy_logs_received_messages_not_twice_by_spy_into_twice
|
207
|
+
setup
|
208
|
+
|
209
|
+
assert_equal(
|
210
|
+
[
|
211
|
+
CrispyReceivedMessage[:hoge, 1, 2, 3],
|
212
|
+
CrispyReceivedMessage[:private_foo, 1],
|
213
|
+
CrispyReceivedMessage[:foo],
|
214
|
+
CrispyReceivedMessage[:hoge, 3, 4, 5],
|
215
|
+
CrispyReceivedMessage[:private_foo, 3],
|
216
|
+
],
|
217
|
+
@subject.received_messages
|
218
|
+
)
|
219
|
+
end
|
220
|
+
|
221
|
+
def test_spy_overrides_stubbed_methods
|
222
|
+
spy_into ObjectClass, stubbed_method2: 'xx', stubbed_method3: 'xxx'
|
223
|
+
|
224
|
+
assert_equal 'before stubbed 1', ObjectClass.stubbed_method1
|
225
|
+
assert_equal 'xx' , ObjectClass.stubbed_method2
|
226
|
+
assert_equal 'xxx' , ObjectClass.stubbed_method3
|
145
227
|
end
|
146
228
|
|
147
229
|
end
|
@@ -154,7 +236,7 @@ class TestCrispy < MiniTest::Test
|
|
154
236
|
|
155
237
|
def setup
|
156
238
|
@returned_spy = spy_into_instances(
|
157
|
-
object_class
|
239
|
+
object_class, method_to_stub1: :stubbed_instance_method1, method_to_stub2: :stubbed_instance_method2
|
158
240
|
)
|
159
241
|
|
160
242
|
@subject = spy_of_instances(object_class)
|
@@ -173,7 +255,30 @@ class TestCrispy < MiniTest::Test
|
|
173
255
|
CrispyWorld.reset
|
174
256
|
end
|
175
257
|
|
258
|
+
module CommonClassSpyTests
|
259
|
+
|
260
|
+
def test_spy_changes_stubbed_method
|
261
|
+
@object_instances.each do|object|
|
262
|
+
assert_equal(:stubbed_instance_method1, object.method_to_stub1)
|
263
|
+
assert_equal(:stubbed_instance_method2, object.method_to_stub2)
|
264
|
+
end
|
265
|
+
end
|
266
|
+
|
267
|
+
def test_spy_overrides_stubbed_methods
|
268
|
+
spy_into_instances object_class, method_to_stub2: 'xx', method_to_stub3: 'xxx'
|
269
|
+
|
270
|
+
@object_instances.each do|object|
|
271
|
+
assert_equal 'method to stub 1 (before stubbed)', object.method_to_stub1
|
272
|
+
assert_equal 'xx' , object.method_to_stub2
|
273
|
+
assert_equal 'xxx' , object.method_to_stub3
|
274
|
+
end
|
275
|
+
end
|
276
|
+
|
277
|
+
end
|
278
|
+
|
176
279
|
class TestReceivedMessage < self
|
280
|
+
include CommonSpyTests
|
281
|
+
include CommonClassSpyTests
|
177
282
|
|
178
283
|
def object_class
|
179
284
|
ObjectClass
|
@@ -246,15 +351,10 @@ class TestCrispy < MiniTest::Test
|
|
246
351
|
assert_equal(2, @subject.count_received(:bar))
|
247
352
|
end
|
248
353
|
|
249
|
-
def test_spy_raises_error_given_non_symbol_as_method_name
|
250
|
-
assert_raises(::TypeError){ @subject.received_once?(nil) }
|
251
|
-
assert_raises(::TypeError){ @subject.received_once?(nil) }
|
252
|
-
assert_raises(::TypeError){ @subject.count_received(nil) }
|
253
|
-
end
|
254
|
-
|
255
354
|
end
|
256
355
|
|
257
356
|
class TestReceivedMessageWithReceiver < self
|
357
|
+
include CommonClassSpyTests
|
258
358
|
|
259
359
|
def object_class
|
260
360
|
ObjectClassNonBasic
|
@@ -275,10 +375,13 @@ class TestCrispy < MiniTest::Test
|
|
275
375
|
def baz
|
276
376
|
end
|
277
377
|
def method_to_stub1
|
278
|
-
|
378
|
+
'method to stub 1 (before stubbed)'
|
279
379
|
end
|
280
380
|
def method_to_stub2
|
281
|
-
|
381
|
+
'method to stub 2 (before stubbed)'
|
382
|
+
end
|
383
|
+
def method_to_stub3
|
384
|
+
'method to stub 3 (before stubbed)'
|
282
385
|
end
|
283
386
|
|
284
387
|
def private_foo a
|
@@ -436,9 +539,9 @@ class TestCrispy < MiniTest::Test
|
|
436
539
|
end
|
437
540
|
|
438
541
|
def test_spy_raises_error_given_non_symbol_as_method_name
|
439
|
-
assert_raises(::TypeError){ @subject.
|
440
|
-
assert_raises(::TypeError){ @subject.
|
441
|
-
assert_raises(::TypeError){ @subject.
|
542
|
+
assert_raises(::TypeError){ @subject.received_with_receiver?(@object_instances[0], nil) }
|
543
|
+
assert_raises(::TypeError){ @subject.received_once_with_receiver?(@object_instances[0], nil) }
|
544
|
+
assert_raises(::TypeError){ @subject.count_received_with_receiver(@object_instances[0], nil) }
|
442
545
|
end
|
443
546
|
|
444
547
|
end
|
@@ -446,6 +549,7 @@ class TestCrispy < MiniTest::Test
|
|
446
549
|
end
|
447
550
|
|
448
551
|
class TestCrispyDouble < TestCrispy
|
552
|
+
|
449
553
|
def setup
|
450
554
|
@expected_hoge = Object.new
|
451
555
|
@expected_foo = Object.new
|
@@ -472,6 +576,32 @@ class TestCrispy < MiniTest::Test
|
|
472
576
|
assert_same @expected_baz, @actual_baz
|
473
577
|
end
|
474
578
|
|
579
|
+
def test_double_can_be_spied
|
580
|
+
assert spied? @double
|
581
|
+
|
582
|
+
assert_same @double.received_messages, spy(@double).received_messages
|
583
|
+
|
584
|
+
assert @double.received?(:hoge)
|
585
|
+
assert @double.received_once?(:hoge, :with, :any, :arguments)
|
586
|
+
assert not(@double.received?(:hoge, 0, 0, 0))
|
587
|
+
assert not(@double.received_once?(:hoge, 0, 0, 0))
|
588
|
+
assert not(@double.received_once?(:hoge))
|
589
|
+
assert_equal 0, @double.count_received(:hoge, 0, 0, 0)
|
590
|
+
assert_equal 1, @double.count_received(:hoge, :with, :any, :arguments)
|
591
|
+
assert_equal 2, @double.count_received(:hoge)
|
592
|
+
|
593
|
+
assert @double.received?(:bar)
|
594
|
+
assert @double.received_once?(:bar)
|
595
|
+
|
596
|
+
assert @double.received_once?(:foo)
|
597
|
+
assert @double.received?(:foo)
|
598
|
+
assert_equal 1, @double.count_received(:foo)
|
599
|
+
|
600
|
+
assert not(@double.received?(:non_used_method))
|
601
|
+
assert not(@double.received_once?(:non_used_method))
|
602
|
+
assert_equal 0, @double.count_received(:non_used_method)
|
603
|
+
end
|
604
|
+
|
475
605
|
end
|
476
606
|
|
477
607
|
end
|
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: crispy
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.
|
4
|
+
version: 0.3.0
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Yamamoto Yuji
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date: 2014-
|
11
|
+
date: 2014-12-31 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: bundler
|
@@ -44,14 +44,14 @@ dependencies:
|
|
44
44
|
requirements:
|
45
45
|
- - "~>"
|
46
46
|
- !ruby/object:Gem::Version
|
47
|
-
version: '5
|
47
|
+
version: '5'
|
48
48
|
type: :development
|
49
49
|
prerelease: false
|
50
50
|
version_requirements: !ruby/object:Gem::Requirement
|
51
51
|
requirements:
|
52
52
|
- - "~>"
|
53
53
|
- !ruby/object:Gem::Version
|
54
|
-
version: '5
|
54
|
+
version: '5'
|
55
55
|
- !ruby/object:Gem::Dependency
|
56
56
|
name: pry
|
57
57
|
requirement: !ruby/object:Gem::Requirement
|
@@ -116,9 +116,7 @@ files:
|
|
116
116
|
- lib/crispy/crispy_internal/const_changer.rb
|
117
117
|
- lib/crispy/crispy_internal/double.rb
|
118
118
|
- lib/crispy/crispy_internal/spy.rb
|
119
|
-
- lib/crispy/crispy_internal/
|
120
|
-
- lib/crispy/crispy_internal/stubber.rb
|
121
|
-
- lib/crispy/crispy_internal/with_stubber.rb
|
119
|
+
- lib/crispy/crispy_internal/spy_base.rb
|
122
120
|
- lib/crispy/crispy_received_message.rb
|
123
121
|
- lib/crispy/crispy_received_message_with_receiver.rb
|
124
122
|
- lib/crispy/crispy_world.rb
|
@@ -146,7 +144,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
146
144
|
version: '0'
|
147
145
|
requirements: []
|
148
146
|
rubyforge_project:
|
149
|
-
rubygems_version: 2.
|
147
|
+
rubygems_version: 2.4.5
|
150
148
|
signing_key:
|
151
149
|
specification_version: 4
|
152
150
|
summary: Test spy for any object.
|
@@ -1,83 +0,0 @@
|
|
1
|
-
require 'crispy/crispy_received_message'
|
2
|
-
|
3
|
-
module Crispy
|
4
|
-
module CrispyInternal
|
5
|
-
module SpyMixin
|
6
|
-
|
7
|
-
BLACK_LISTED_METHODS = [
|
8
|
-
:__CRISPY_CLASS_SPY__,
|
9
|
-
:__CRISPY_SPY__,
|
10
|
-
]
|
11
|
-
|
12
|
-
def received_messages
|
13
|
-
raise NotImplementedError
|
14
|
-
end
|
15
|
-
|
16
|
-
def erase_log
|
17
|
-
raise NotImplementedError
|
18
|
-
end
|
19
|
-
|
20
|
-
def stop
|
21
|
-
@spying = false
|
22
|
-
end
|
23
|
-
|
24
|
-
def restart
|
25
|
-
@spying = true
|
26
|
-
end
|
27
|
-
|
28
|
-
COMMON_RECEIVED_MESSAGE_METHODS_DEFINITION = {
|
29
|
-
'received?' => 'include? %s',
|
30
|
-
'received_once?' => 'one? {|self_thing| self_thing == %s }',
|
31
|
-
'count_received' => 'count %s',
|
32
|
-
}
|
33
|
-
|
34
|
-
COMMON_RECEIVED_MESSAGE_METHODS_DEFINITION.each do|method_name, core_definition|
|
35
|
-
binding.eval(<<-END, __FILE__, (__LINE__ + 1))
|
36
|
-
def #{method_name} received_method_name, *received_arguments, &received_block
|
37
|
-
assert_symbol! received_method_name
|
38
|
-
if received_arguments.empty? and received_block.nil?
|
39
|
-
received_messages.map(&:method_name).#{sprintf(core_definition, 'received_method_name')}
|
40
|
-
else
|
41
|
-
received_message = ::Crispy::CrispyReceivedMessage.new(
|
42
|
-
received_method_name, *received_arguments, &received_block
|
43
|
-
)
|
44
|
-
received_messages.#{sprintf(core_definition, 'received_message')}
|
45
|
-
end
|
46
|
-
end
|
47
|
-
END
|
48
|
-
end
|
49
|
-
|
50
|
-
def prepend_features klass
|
51
|
-
super
|
52
|
-
|
53
|
-
without_black_listed_methods(klass.public_instance_methods).each do|method_name|
|
54
|
-
self.module_eval { define_wrapper(method_name) }
|
55
|
-
end
|
56
|
-
klass.protected_instance_methods.each do|method_name|
|
57
|
-
self.module_eval { protected define_wrapper(method_name) }
|
58
|
-
end
|
59
|
-
klass.private_instance_methods.each do|method_name|
|
60
|
-
self.module_eval { private define_wrapper(method_name) }
|
61
|
-
end
|
62
|
-
end
|
63
|
-
private :prepend_features
|
64
|
-
|
65
|
-
def without_black_listed_methods method_names
|
66
|
-
method_names.reject {|method_name| BLACK_LISTED_METHODS.include? method_name }
|
67
|
-
end
|
68
|
-
private :without_black_listed_methods
|
69
|
-
|
70
|
-
def initialize_spy
|
71
|
-
@spying = true
|
72
|
-
end
|
73
|
-
private :initialize_spy
|
74
|
-
|
75
|
-
def assert_symbol! maybe_symbol
|
76
|
-
unless maybe_symbol.respond_to?(:to_sym) && maybe_symbol.to_sym.instance_of?(::Symbol)
|
77
|
-
raise TypeError, "TypeError: no implicit conversion from #{maybe_symbol.inspect} to symbol"
|
78
|
-
end
|
79
|
-
end
|
80
|
-
|
81
|
-
end
|
82
|
-
end
|
83
|
-
end
|
@@ -1,31 +0,0 @@
|
|
1
|
-
module Crispy
|
2
|
-
module CrispyInternal
|
3
|
-
class Stubber < Module
|
4
|
-
public :prepend_features
|
5
|
-
|
6
|
-
def initialize stubs_map = {}
|
7
|
-
super()
|
8
|
-
stub stubs_map
|
9
|
-
end
|
10
|
-
|
11
|
-
NOT_SPECIFIED = ::Object.new
|
12
|
-
|
13
|
-
def stub method_name_or_hash, returned_value = NOT_SPECIFIED, &definition
|
14
|
-
case method_name_or_hash
|
15
|
-
when Hash
|
16
|
-
hash = method_name_or_hash
|
17
|
-
hash.each do|method_name, value|
|
18
|
-
stub method_name, value
|
19
|
-
end
|
20
|
-
when ::Symbol, ::String
|
21
|
-
self.module_exec method_name_or_hash do|method_name|
|
22
|
-
# TODO: should not ignore arguments?
|
23
|
-
define_method(method_name) {|*_arguments| returned_value }
|
24
|
-
end
|
25
|
-
end
|
26
|
-
self
|
27
|
-
end
|
28
|
-
|
29
|
-
end
|
30
|
-
end
|
31
|
-
end
|
@@ -1,24 +0,0 @@
|
|
1
|
-
require 'crispy/crispy_internal/stubber'
|
2
|
-
|
3
|
-
module Crispy
|
4
|
-
module CrispyInternal
|
5
|
-
module WithStubber
|
6
|
-
|
7
|
-
def initialize_stubber stubs_map = {}
|
8
|
-
@__CRISPY_STUBBER__ = Stubber.new(stubs_map)
|
9
|
-
end
|
10
|
-
private :initialize_stubber
|
11
|
-
|
12
|
-
def prepend_stubber klass
|
13
|
-
@__CRISPY_STUBBER__.prepend_features klass
|
14
|
-
end
|
15
|
-
private :prepend_stubber
|
16
|
-
|
17
|
-
def stub *arguments, &definition
|
18
|
-
@__CRISPY_STUBBER__.stub(*arguments, &definition)
|
19
|
-
self
|
20
|
-
end
|
21
|
-
|
22
|
-
end
|
23
|
-
end
|
24
|
-
end
|