crispy 0.2.0 → 0.3.0
Sign up to get free protection for your applications and to get access to all the features.
- 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
|