spy 0.2.4 → 0.2.5

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