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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: a5a304875b84ee789d83ba6420f4b57a2cd769d3
4
- data.tar.gz: 50dddf1394a740c51c7a15dbfd5d4bd3227388b8
3
+ metadata.gz: 906a71958302bc1652930a6e9f1c5aa237eaa233
4
+ data.tar.gz: 27951041fba2e5b62fb3d78d3b26311455ac5863
5
5
  SHA512:
6
- metadata.gz: 469caadde79084134b26358634de2830d156233e938b4ad2c1a21dd6653d0bd50d019530e6c5ad80bcf9b5e59526b135572bc8939b5d160b2f4a3f10129d79d8
7
- data.tar.gz: 902018025f544c073758117e5072896cb7dbf5b6cbc2a41f52b6116036a485a24ff23090e36718d94fdabe0ca7b9a27c21fa18db2bf61be6e255247029c2c58d
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-preview1
8
- - 2.1.3
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
@@ -1,4 +1,4 @@
1
- Copyright (c) 2014 Yamamoto Yuji
1
+ Copyright (c) 2014 Yuji Yamamoto
2
2
 
3
3
  MIT License
4
4
 
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 any object by using `prepend` (Sorry, it runs by Ruby 2.0 or higher!)
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
- >> spy = spy_into object
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 to spy after called methods with `with_receiver` (or `with_receiver?`) prefix.
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
- >> false
204
+ => false
179
205
  ```
180
206
 
181
207
  If you want to restart spying, use `restart` method literally.
182
208
 
183
209
  ```ruby
184
- >> spy_into_instances(YourCoolClass::Again).restart
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
- >> true
214
+ => true
189
215
  ```
190
216
 
191
- ### Stub Methods of a Spy
217
+ ### Stub Methods of Instances of a Class
192
218
 
193
219
  ```ruby
194
- >> spy(object).stub(:your_cool_method, 'Awesome!')
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
- >> spy(object).stub(your_lovely_method: 'I love this method!', your_finalizer: 'Finalized!')
199
- >> object.your_lovely_method
200
- => "I love this method!"
201
- >> object.your_finalizer
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.4"
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/spy_mixin'
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 < ::Module
8
- include SpyMixin
9
- #include WithStubber
6
+ class ClassSpy < SpyBase
10
7
 
11
8
  @registry = {}
12
9
 
13
- def initialize klass#, stubs_map = {}
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
- #initialize_stubber stubs_map
23
- #prepend_stubber klass
13
+ super
24
14
 
25
- prepend_features klass
26
- self.class.register spy: spy, of_class: klass
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 append_received_message_with_receiver receiver, method_name, *arguments, &attached_block
67
- if @spying
68
- @received_messages_with_receiver <<
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
- initialize_stubber(name_or_stubs_map)
8
+ @spy = ::Crispy.spy_into(self, name_or_stubs_map)
12
9
  else
13
10
  @name = name_or_stubs_map
14
- initialize_stubber(stubs_map)
11
+ @spy = ::Crispy.spy_into(self, stubs_map)
15
12
  end
16
- singleton_class =
17
- class << self
18
- self
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
- prepend_stubber singleton_class
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/spy_mixin'
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 < ::Module
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
- super() do
15
- spy = self
11
+ spy = self
12
+ module_eval do
16
13
  define_method(:__CRISPY_SPY__) { spy }
17
14
  end
18
15
 
19
16
  @received_messages = []
20
- initialize_spy
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
- prepend_features singleton_class
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 append_received_message method_name, *arguments, &attached_block
37
- if @spying
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 define_wrapper method_name
44
- define_method method_name do|*arguments, &attached_block|
45
- __CRISPY_SPY__.append_received_message(method_name, *arguments, &attached_block)
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 :define_wrapper
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}[#{(@method_name).inspect}, *#{@arguments.inspect}]>"
27
+ "#<#{CLASS_NAME}[#{@method_name.inspect}, *#{@arguments.inspect}]>"
28
28
  end
29
29
 
30
30
  alias inspect to_s
@@ -1,3 +1,3 @@
1
1
  module Crispy
2
- VERSION = "0.2.0"
2
+ VERSION = "0.3.0"
3
3
  end
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#, stubs_map = {}
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.of_class(klass)
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
- fail "Not stubbed actually! The test fails."
42
+ 'method to stub 1 (before stubbed)'
29
43
  end
30
44
  def method_to_stub2
31
- fail "Not stubbed actually! The test fails."
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
- def test_spy_is_also_returned_by_spy_into_method
138
- assert_same @subject, @returned_spy
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 test_spy_raises_error_given_non_symbol_as_method_name
142
- assert_raises(::TypeError){ @subject.received_once?(nil) }
143
- assert_raises(::TypeError){ @subject.received_once?(nil) }
144
- assert_raises(::TypeError){ @subject.count_received(nil) }
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#, method_to_stub1: :stubbed_instance_method1, method_to_stub2: :stubbed_instance_method2
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
- fail "Not stubbed actually! The test fails."
378
+ 'method to stub 1 (before stubbed)'
279
379
  end
280
380
  def method_to_stub2
281
- fail "Not stubbed actually! The test fails."
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.received_once?(@object_instances[0], nil) }
440
- assert_raises(::TypeError){ @subject.received_once?(@object_instances[0], nil) }
441
- assert_raises(::TypeError){ @subject.count_received(@object_instances[0], nil) }
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.2.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-02 00:00:00.000000000 Z
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.4'
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.4'
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/spy_mixin.rb
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.2.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