spy 0.2.4 → 0.2.5

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.
data/.travis.yml CHANGED
@@ -1,5 +1,11 @@
1
1
  language: ruby
2
2
  rvm:
3
- - "1.9.3"
4
- - "2.0.0"
3
+ - 1.9.3
4
+ - 2.0.0
5
5
  - jruby-19mode
6
+ - ruby-head
7
+ - rbx-19mode
8
+ matrix:
9
+ allow_failures:
10
+ - rvm: ruby-head
11
+ - rvm: rbx-19mode
data/README.md CHANGED
@@ -1,4 +1,8 @@
1
- # Spy [![Build Status](https://travis-ci.org/ryanong/spy.png?branch=master)](https://travis-ci.org/ryanong/spy) [![Gem Version](https://badge.fury.io/rb/spy.png)](http://badge.fury.io/rb/spy)
1
+ # Spy
2
+ [![Gem Version](https://badge.fury.io/rb/spy.png)](http://badge.fury.io/rb/spy)
3
+ [![Build Status](https://travis-ci.org/ryanong/spy.png?branch=master)](https://travis-ci.org/ryanong/spy)
4
+ [![Coverage Status](https://coveralls.io/repos/ryanong/spy/badge.png?branch=master)](https://coveralls.io/r/ryanong/spy)
5
+
2
6
 
3
7
  [Docs](http://rdoc.info/gems/spy/frames)
4
8
 
@@ -40,6 +44,7 @@ Fail faster, code faster.
40
44
  * #with is not supported
41
45
  * you can usually just check the call logs.
42
46
  * if you do need to use this. It is probably a code smell. You either need to abstract your method more or add separate tests.
47
+ * you use `mock_model` and `stub_model` (I want to impliment this soon)
43
48
 
44
49
  ## Installation
45
50
 
@@ -75,7 +80,7 @@ Spy will raise an error if you try to stub on a method that doesn't exist.
75
80
  You can force the creation of a stub on method that didn't exist but it really isn't suggested.
76
81
 
77
82
  ```ruby
78
- Spy.new(book, :flamethrower).hook(force:true).and_return("burnninante")
83
+ Spy::Subroutine.new(book, :flamethrower).hook(force:true).and_return("burnninante")
79
84
  ```
80
85
 
81
86
  You can also stub instance methods of Classes and Modules. This is equivalent to
@@ -176,6 +181,19 @@ In `test_helper.rb`
176
181
  ```ruby
177
182
  require "spy"
178
183
  MiniTest::TestCase.add_teardown_hook { Spy.teardown }
184
+
185
+
186
+ class TestBook < MiniTest::Unit::TestCase
187
+ def test_title
188
+ book = book.new
189
+ title_spy = spy.on(book, title)
190
+ book.title
191
+ book.title
192
+
193
+ assert title_spy.has_been_called?
194
+ assert_equal 2, title_spy.calls.count
195
+ end
196
+ end
179
197
  ```
180
198
 
181
199
  ### Rspec
@@ -189,6 +207,18 @@ RSpec.configure do |c|
189
207
  c.after { Spy.teardown }
190
208
  c.mock_with :absolutely_nothing # this is completely optional.
191
209
  end
210
+
211
+ describe Book do
212
+ it "title can be called" do
213
+ book = book.new
214
+ title_spy = spy.on(book, title)
215
+ book.title
216
+ book.title
217
+
218
+ expect(title_spy).to have_been_called
219
+ expect(title_spy.calls.count).to eq(2)
220
+ end
221
+ end
192
222
  ```
193
223
 
194
224
  ### Test::Unit
data/lib/spy/agency.rb CHANGED
@@ -10,7 +10,6 @@ module Spy
10
10
  clear!
11
11
  end
12
12
 
13
-
14
13
  # given a spy ID it will return the associated spy
15
14
  # @param id [Integer] spy object id
16
15
  # @return [Nil, Subroutine, Constant, Double]
@@ -22,11 +21,12 @@ module Spy
22
21
  # @param spy [Subroutine, Constant, Double]
23
22
  # @return [spy]
24
23
  def recruit(spy)
24
+ raise AlreadyStubbedError if @spies[spy.object_id]
25
25
  case spy
26
26
  when Subroutine, Constant, Double
27
27
  @spies[spy.object_id] = spy
28
28
  else
29
- raise "Not a spy"
29
+ raise ArgumentError, "#{spy}, was not a spy"
30
30
  end
31
31
  end
32
32
 
@@ -34,11 +34,12 @@ module Spy
34
34
  # @param spy [Subroutine, Constant, Double]
35
35
  # @return [spy]
36
36
  def retire(spy)
37
+ raise NoSpyError unless @spies[spy.object_id]
37
38
  case spy
38
39
  when Subroutine, Constant, Double
39
40
  @spies.delete(spy.object_id)
40
41
  else
41
- raise "Not a spy"
42
+ raise ArgumentError, "#{spy}, was not a spy"
42
43
  end
43
44
  end
44
45
 
@@ -50,7 +51,7 @@ module Spy
50
51
  when Subroutine, Constant, Double
51
52
  @spies.has_key?(spy.object_id)
52
53
  else
53
- raise "Not a spy"
54
+ raise ArgumentError, "#{spy}, was not a spy"
54
55
  end
55
56
  end
56
57
 
data/lib/spy/constant.rb CHANGED
@@ -16,8 +16,8 @@ module Spy
16
16
  # @param base_module [Module] the module this spy should be on
17
17
  # @param constant_name [Symbol] the constant this spy is watching
18
18
  def initialize(base_module, constant_name)
19
- raise "#{base_module.inspect} is not a kind of Module" unless base_module.is_a? Module
20
- raise "#{constant_name.inspect} is not a kind of Symbol" unless constant_name.is_a? Symbol
19
+ raise ArgumentError, "#{base_module.inspect} is not a kind of Module" unless base_module.is_a? Module
20
+ raise ArgumentError, "#{constant_name.inspect} is not a kind of Symbol" unless constant_name.is_a? Symbol
21
21
  @base_module, @constant_name = base_module, constant_name.to_sym
22
22
  @original_value = @new_value = @previously_defined = nil
23
23
  end
@@ -97,7 +97,7 @@ module Spy
97
97
  end
98
98
 
99
99
  class << self
100
- # creates a new constant spy and hooks the constant
100
+ # finds existing spy or creates a new constant spy and hooks the constant
101
101
  # @return [Constant]
102
102
  def on(base_module, constant_name)
103
103
  new(base_module, constant_name).hook
@@ -107,7 +107,9 @@ module Spy
107
107
  # from the module
108
108
  # @return [Constant]
109
109
  def off(base_module, constant_name)
110
- get(base_module, constant_name).unhook
110
+ spy = get(base_module, constant_name)
111
+ raise NoSpyError, "#{constant_name} was not spied on #{base_module}" unless spy
112
+ spy.unhook
111
113
  end
112
114
 
113
115
  # retrieves the spy for given constnat and module or returns nil
@@ -115,7 +117,7 @@ module Spy
115
117
  def get(base_module, constant_name)
116
118
  nest = Nest.get(base_module)
117
119
  if nest
118
- nest.hooked_constants[constant_name]
120
+ nest.get(constant_name)
119
121
  end
120
122
  end
121
123
  end
@@ -0,0 +1,33 @@
1
+ module Spy
2
+ class Error < StandardError; end
3
+
4
+ class AlreadyStubbedError < Error
5
+ def message
6
+ "Spy is already stubbed."
7
+ end
8
+ end
9
+
10
+ class AlreadyHookedError < Error
11
+ def message
12
+ "Spy is already hooked."
13
+ end
14
+ end
15
+
16
+ class NotHookedError < Error
17
+ def message
18
+ "Spy was not hooked."
19
+ end
20
+ end
21
+
22
+ class NeverHookedError < Error
23
+ def message
24
+ "Spy was never hooked."
25
+ end
26
+ end
27
+
28
+ class NoSpyError < Error
29
+ def message
30
+ "Spy could not be found"
31
+ end
32
+ end
33
+ end
data/lib/spy/nest.rb CHANGED
@@ -5,26 +5,26 @@ module Spy
5
5
  # @!attribute [r] base_module
6
6
  # @return [Module] The module that the Nest is managing
7
7
  #
8
- # @!attribute [r] hooked_constants
8
+ # @!attribute [r] constant_spies
9
9
  # @return [Hash<Symbol, Constant>] The module that the Nest is managing
10
10
 
11
11
 
12
- attr_reader :base_module, :hooked_constants
12
+ attr_reader :base_module
13
13
 
14
14
  def initialize(base_module)
15
- raise "#{base_module} is not a kind of Module" unless base_module.is_a?(Module)
15
+ raise ArgumentError, "#{base_module} is not a kind of Module" unless base_module.is_a?(Module)
16
16
  @base_module = base_module
17
- @hooked_constants = {}
17
+ @constant_spies = {}
18
18
  end
19
19
 
20
20
  # records that the spy is hooked
21
21
  # @param spy [Constant]
22
22
  # @return [self]
23
23
  def add(spy)
24
- if @hooked_constants[spy.constant_name]
25
- raise "#{spy.constant_name} has already been stubbed"
24
+ if @constant_spies[spy.constant_name]
25
+ raise AlreadyStubbedError, "#{spy.constant_name} has already been stubbed"
26
26
  else
27
- @hooked_constants[spy.constant_name] = spy
27
+ @constant_spies[spy.constant_name] = spy
28
28
  end
29
29
  self
30
30
  end
@@ -33,19 +33,32 @@ module Spy
33
33
  # @param spy [Constant]
34
34
  # @return [self]
35
35
  def remove(spy)
36
- if @hooked_constants[spy.constant_name] == spy
37
- @hooked_constants.delete(spy.constant_name)
36
+ if @constant_spies[spy.constant_name] == spy
37
+ @constant_spies.delete(spy.constant_name)
38
38
  else
39
- raise "#{spy.constant_name} was never added"
39
+ raise NoSpyError, "#{spy.constant_name} was not stubbed on #{base_module.name}"
40
40
  end
41
41
  self
42
42
  end
43
43
 
44
+ # returns a spy if the constant was added
45
+ # @param constant_name [Symbol]
46
+ # @return [Constant, nil]
47
+ def get(constant_name)
48
+ @constant_spies[constant_name]
49
+ end
50
+
44
51
  # checks to see if a given constant is hooked
45
52
  # @param constant_name [Symbol]
46
53
  # @return [Boolean]
47
54
  def hooked?(constant_name)
48
- !!@hooked_constants[constant_name]
55
+ !!get(constant_name)
56
+ end
57
+
58
+ # list all the constants that are being stubbed
59
+ # @return [Array]
60
+ def hooked_constants
61
+ @constant_spies.keys
49
62
  end
50
63
 
51
64
  class << self
@@ -56,7 +69,6 @@ module Spy
56
69
  all[base_module.name]
57
70
  end
58
71
 
59
-
60
72
  # retrieves the nest for a given module or creates it
61
73
  # @param base_module [Module]
62
74
  # @return [Nest]
@@ -40,7 +40,7 @@ module Spy
40
40
  # @option opts [Symbol<:public, :protected, :private>] visibility overrides visibility with whatever method is given
41
41
  # @return [self]
42
42
  def hook(opts = {})
43
- raise "#{base_object} method '#{method_name}' has already been hooked" if hooked?
43
+ raise AlreadyHookedError, "#{base_object} method '#{method_name}' has already been hooked" if self.class.get(base_object, method_name, singleton_method)
44
44
 
45
45
  @hook_opts = opts
46
46
  @original_method_visibility = method_visibility_of(method_name)
@@ -66,7 +66,7 @@ module Spy
66
66
  # unhooks method from object
67
67
  # @return [self]
68
68
  def unhook
69
- raise "'#{method_name}' method has not been hooked" unless hooked?
69
+ raise NeverHookedError, "'#{method_name}' method has not been hooked" unless hooked?
70
70
 
71
71
  if original_method && method_owner == original_method.owner
72
72
  method_owner.send(:define_method, method_name, original_method)
@@ -111,7 +111,7 @@ module Spy
111
111
  if value.is_a?(Hash) && value.has_key?(:force)
112
112
  @do_not_check_plan_arity = !!value[:force]
113
113
  elsif !value.nil?
114
- raise ArgumentError.new("value and block conflict. Choose one")
114
+ raise ArgumentError, "value and block conflict. Choose one"
115
115
  end
116
116
 
117
117
  @plan = Proc.new
@@ -184,7 +184,7 @@ module Spy
184
184
  # if the method was called it will return true
185
185
  # @return [Boolean]
186
186
  def has_been_called?
187
- raise "was never hooked" unless @was_hooked
187
+ raise NeverHookedError unless @was_hooked
188
188
  calls.size > 0
189
189
  end
190
190
 
@@ -192,7 +192,7 @@ module Spy
192
192
  # @param args Arguments that should have been sent to the method
193
193
  # @return [Boolean]
194
194
  def has_been_called_with?(*args)
195
- raise "was never hooked" unless @was_hooked
195
+ raise NeverHookedError unless @was_hooked
196
196
  calls.any? do |call_log|
197
197
  call_log.args == args
198
198
  end
@@ -305,7 +305,23 @@ module Spy
305
305
  end
306
306
 
307
307
  class << self
308
- # retrieve the method spy from an object
308
+
309
+ # retrieve the method spy from an object or create a new one
310
+ # @param base_object
311
+ # @param method_name [Symbol]
312
+ # @param singleton_method [Boolean] this a singleton method or a instance method?
313
+ # @return [Array<Subroutine>]
314
+ def on(base_object, method_name, singleton_method = true)
315
+ new(base_object, method_name, singleton_method).hook
316
+ end
317
+
318
+ def off(base_object, method_name, singleton_method = true)
319
+ spy = get(base_object, method_name, singleton_method = true)
320
+ raise NoSpyError, "#{method_name} was not spied on #{base_object}" unless spy
321
+ spy.unhook
322
+ end
323
+
324
+ # retrieve the method spy from an object or return nil
309
325
  # @param base_object
310
326
  # @param method_name [Symbol]
311
327
  # @param singleton_method [Boolean] this a singleton method or a instance method?
data/lib/spy/version.rb CHANGED
@@ -1,3 +1,3 @@
1
1
  module Spy
2
- VERSION = "0.2.4"
2
+ VERSION = "0.2.5"
3
3
  end
data/lib/spy.rb CHANGED
@@ -3,6 +3,7 @@ require "spy/agency"
3
3
  require "spy/call_log"
4
4
  require "spy/constant"
5
5
  require "spy/double"
6
+ require "spy/exceptions"
6
7
  require "spy/nest"
7
8
  require "spy/subroutine"
8
9
  require "spy/version"
@@ -32,7 +33,7 @@ module Spy
32
33
  if spy
33
34
  spy.unhook
34
35
  else
35
- raise "#{spy.inspect} was not found to be hooked"
36
+ raise NoSpyError, "#{method_name} was not hooked on #{base_object.inspect}."
36
37
  end
37
38
  end
38
39
 
@@ -54,7 +55,7 @@ module Spy
54
55
  if spy
55
56
  spy.unhook
56
57
  else
57
- raise "#{spy.inspect} was not found to be hooked"
58
+ raise NoSpyError, "#{method_name} was not hooked on #{base_object.inspect}."
58
59
  end
59
60
  end
60
61
 
@@ -79,7 +80,7 @@ module Spy
79
80
  Constant.on(base_module, name).and_return(result)
80
81
  end
81
82
  else
82
- raise ArgumentError.new "#{constant_name.class} is an invalid input, #on only accepts Symbol, and Hash"
83
+ raise ArgumentError, "#{constant_name.class} is an invalid input, #on only accepts Symbol, and Hash"
83
84
  end
84
85
  end.flatten
85
86
 
@@ -98,7 +99,7 @@ module Spy
98
99
 
99
100
  spies = constant_names.map do |constant_name|
100
101
  unless constant_name.is_a?(Symbol)
101
- raise ArgumentError.new "#{constant_name.class} is an invalid input, #on only accepts Symbol, and Hash"
102
+ raise ArgumentError, "#{constant_name.class} is an invalid input, #on only accepts Symbol, and Hash"
102
103
  end
103
104
  Constant.off(base_module, constant_name)
104
105
  end
@@ -148,16 +149,16 @@ module Spy
148
149
 
149
150
  private
150
151
 
151
- def create_and_hook_spy(base_object, method_name, singleton_method = true, hook_opts = {})
152
+ def create_and_hook_spy(base_object, method_name, singleton_method = true)
152
153
  case method_name
153
154
  when String, Symbol
154
- Subroutine.new(base_object, method_name, singleton_method).hook(hook_opts)
155
+ Subroutine.on(base_object, method_name, singleton_method)
155
156
  when Hash
156
157
  method_name.map do |name, result|
157
- create_and_hook_spy(base_object, name, singleton_method, hook_opts).and_return(result)
158
+ Subroutine.on(base_object, name, singleton_method).and_return(result)
158
159
  end
159
160
  else
160
- raise ArgumentError.new "#{method_name.class} is an invalid input, #on only accepts String, Symbol, and Hash"
161
+ raise ArgumentError, "#{method_name.class} is an invalid input, #on only accepts String, Symbol, and Hash"
161
162
  end
162
163
  end
163
164
  end
@@ -444,7 +444,7 @@ module Spy
444
444
 
445
445
  context 'when used in conjunction with a `dup`' do
446
446
  it "doesn't cause an infinite loop" do
447
- Object.any_instance.stub(:some_method)
447
+ Spy::Subroutine.new(Object, :some_method, false).hook(force: true)
448
448
  o = Object.new
449
449
  o.some_method
450
450
  expect { o.dup.some_method }.to_not raise_error(SystemStackError)
@@ -454,7 +454,7 @@ module Spy
454
454
  klass = Class.new do
455
455
  undef_method :dup
456
456
  end
457
- klass.any_instance
457
+ Spy::Subroutine.new(Object, :some_method, false).hook(force: true)
458
458
  end
459
459
 
460
460
  it "doesn't fail when dup accepts parameters" do
@@ -463,7 +463,7 @@ module Spy
463
463
  end
464
464
  end
465
465
 
466
- klass.any_instance
466
+ Spy::Subroutine.new(Object, :some_method, false).hook(force: true)
467
467
 
468
468
  expect { klass.new.dup('Dup dup dup') }.to_not raise_error(ArgumentError)
469
469
  end
data/spy.gemspec CHANGED
@@ -23,4 +23,5 @@ Gem::Specification.new do |gem|
23
23
  gem.add_development_dependency('minitest', '>= 4.5.0')
24
24
  gem.add_development_dependency('rspec-core')
25
25
  gem.add_development_dependency('rspec-expectations')
26
+ gem.add_development_dependency('coveralls')
26
27
  end
@@ -226,8 +226,15 @@ module Spy
226
226
  def test_spy_get_can_retrieve_a_spy
227
227
  pen_write_spy = spy_on(@pen, :write).and_return(:hello)
228
228
  assert_equal :hello, @pen.write(:world)
229
- assert_equal pen_write_spy, Subroutine.get(@pen, :write)
230
229
  assert Subroutine.get(@pen, :write).has_been_called?
230
+ assert_same pen_write_spy, Subroutine.get(@pen, :write)
231
+ end
232
+
233
+ def test_spy_hook_raises_an_error_on_an_already_hooked_method
234
+ spy_on(@pen, :write)
235
+ assert_raises AlreadyHookedError do
236
+ spy_on(@pen, :write)
237
+ end
231
238
  end
232
239
  end
233
240
  end
data/test/test_helper.rb CHANGED
@@ -2,6 +2,8 @@ require 'bundler/setup'
2
2
  require 'minitest/autorun'
3
3
  require 'pry'
4
4
  require 'pry-nav'
5
+ require 'coveralls'
6
+ Coveralls.wear!
5
7
 
6
8
  require 'spy'
7
9
 
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: spy
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.2.4
4
+ version: 0.2.5
5
5
  prerelease:
6
6
  platform: ruby
7
7
  authors:
@@ -9,7 +9,7 @@ authors:
9
9
  autorequire:
10
10
  bindir: bin
11
11
  cert_chain: []
12
- date: 2013-02-28 00:00:00.000000000 Z
12
+ date: 2013-03-10 00:00:00.000000000 Z
13
13
  dependencies:
14
14
  - !ruby/object:Gem::Dependency
15
15
  name: pry
@@ -91,6 +91,22 @@ dependencies:
91
91
  - - ! '>='
92
92
  - !ruby/object:Gem::Version
93
93
  version: '0'
94
+ - !ruby/object:Gem::Dependency
95
+ name: coveralls
96
+ requirement: !ruby/object:Gem::Requirement
97
+ none: false
98
+ requirements:
99
+ - - ! '>='
100
+ - !ruby/object:Gem::Version
101
+ version: '0'
102
+ type: :development
103
+ prerelease: false
104
+ version_requirements: !ruby/object:Gem::Requirement
105
+ none: false
106
+ requirements:
107
+ - - ! '>='
108
+ - !ruby/object:Gem::Version
109
+ version: '0'
94
110
  description: A simple modern mocking library that uses the spy pattern and checks
95
111
  method's existence and arity.
96
112
  email:
@@ -112,6 +128,7 @@ files:
112
128
  - lib/spy/constant.rb
113
129
  - lib/spy/core_ext/marshal.rb
114
130
  - lib/spy/double.rb
131
+ - lib/spy/exceptions.rb
115
132
  - lib/spy/nest.rb
116
133
  - lib/spy/subroutine.rb
117
134
  - lib/spy/version.rb