spy 0.2.5 → 0.3.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- data/README.md +15 -8
- data/lib/spy/agency.rb +3 -6
- data/lib/spy/base.rb +4 -0
- data/lib/spy/constant.rb +1 -1
- data/lib/spy/exceptions.rb +10 -10
- data/lib/spy/mock.rb +122 -0
- data/lib/spy/subroutine.rb +4 -7
- data/lib/spy/version.rb +1 -1
- data/lib/spy.rb +22 -7
- data/spec/spy/mutate_const_spec.rb +0 -8
- data/test/integration/test_mocking.rb +25 -0
- data/test/integration/test_subroutine_spying.rb +0 -1
- data/test/spy/test_mock.rb +88 -0
- metadata +8 -7
- data/lib/spy/double.rb +0 -28
- data/spec/spy/mock_spec.rb +0 -222
- data/test/spy/test_double.rb +0 -23
data/README.md
CHANGED
@@ -6,7 +6,7 @@
|
|
6
6
|
|
7
7
|
[Docs](http://rdoc.info/gems/spy/frames)
|
8
8
|
|
9
|
-
Spy is a lightweight stubbing framework with support for method spies, constant stubs, and object
|
9
|
+
Spy is a lightweight stubbing framework with support for method spies, constant stubs, and object mocks.
|
10
10
|
|
11
11
|
Spy was designed for 1.9.3+.
|
12
12
|
|
@@ -44,6 +44,7 @@ Fail faster, code faster.
|
|
44
44
|
* #with is not supported
|
45
45
|
* you can usually just check the call logs.
|
46
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 want to use dumb double, Spy has smart mocks, they are better
|
47
48
|
* you use `mock_model` and `stub_model` (I want to impliment this soon)
|
48
49
|
|
49
50
|
## Installation
|
@@ -93,20 +94,26 @@ Book.new(title: "Siddhartha").title #=> "Cannery Row"
|
|
93
94
|
Book.new(title: "The Big Cheese").title #=> "Cannery Row"
|
94
95
|
```
|
95
96
|
|
96
|
-
### Test
|
97
|
+
### Test Mocks
|
97
98
|
|
98
|
-
A test
|
99
|
+
A test mock is an object that quacks like a given class but will raise an error
|
100
|
+
when the method is not stubbed. You can spy on the classes and call through to
|
101
|
+
the original method.
|
99
102
|
|
100
103
|
```ruby
|
101
|
-
Spy.
|
102
|
-
|
104
|
+
book = Spy.mock(Book)
|
105
|
+
Spy.on(book, first_name: "Neil", last_name: "Gaiman")
|
106
|
+
Spy.on(book, :author).and_call_through
|
107
|
+
book.author #=> "Neil Gaiman"
|
103
108
|
|
104
|
-
|
109
|
+
book.responds_to? :title #=> true
|
110
|
+
book.title #=> Spy::NeverHookedError: 'title' was never hooked on mock spy.
|
111
|
+
```
|
105
112
|
|
106
|
-
|
113
|
+
To stub methods during instantiation just add arguments.
|
107
114
|
|
108
115
|
```ruby
|
109
|
-
Spy.
|
116
|
+
book = Spy.mock(book, :first_name, author: "Neil Gaiman")
|
110
117
|
```
|
111
118
|
|
112
119
|
### Arbitrary Handling
|
data/lib/spy/agency.rb
CHANGED
@@ -22,8 +22,7 @@ module Spy
|
|
22
22
|
# @return [spy]
|
23
23
|
def recruit(spy)
|
24
24
|
raise AlreadyStubbedError if @spies[spy.object_id]
|
25
|
-
|
26
|
-
when Subroutine, Constant, Double
|
25
|
+
if spy.is_a? Base
|
27
26
|
@spies[spy.object_id] = spy
|
28
27
|
else
|
29
28
|
raise ArgumentError, "#{spy}, was not a spy"
|
@@ -35,8 +34,7 @@ module Spy
|
|
35
34
|
# @return [spy]
|
36
35
|
def retire(spy)
|
37
36
|
raise NoSpyError unless @spies[spy.object_id]
|
38
|
-
|
39
|
-
when Subroutine, Constant, Double
|
37
|
+
if spy.is_a? Base
|
40
38
|
@spies.delete(spy.object_id)
|
41
39
|
else
|
42
40
|
raise ArgumentError, "#{spy}, was not a spy"
|
@@ -47,8 +45,7 @@ module Spy
|
|
47
45
|
# @param spy [Subroutine, Constant, Double]
|
48
46
|
# @return [Boolean]
|
49
47
|
def active?(spy)
|
50
|
-
|
51
|
-
when Subroutine, Constant, Double
|
48
|
+
if spy.is_a? Base
|
52
49
|
@spies.has_key?(spy.object_id)
|
53
50
|
else
|
54
51
|
raise ArgumentError, "#{spy}, was not a spy"
|
data/lib/spy/base.rb
ADDED
data/lib/spy/constant.rb
CHANGED
data/lib/spy/exceptions.rb
CHANGED
@@ -2,32 +2,32 @@ module Spy
|
|
2
2
|
class Error < StandardError; end
|
3
3
|
|
4
4
|
class AlreadyStubbedError < Error
|
5
|
-
def
|
6
|
-
"Spy is already stubbed."
|
5
|
+
def to_s
|
6
|
+
@mesg || "Spy is already stubbed."
|
7
7
|
end
|
8
8
|
end
|
9
9
|
|
10
10
|
class AlreadyHookedError < Error
|
11
|
-
def
|
12
|
-
"Spy is already hooked."
|
11
|
+
def to_s
|
12
|
+
@mesg || "Spy is already hooked."
|
13
13
|
end
|
14
14
|
end
|
15
15
|
|
16
16
|
class NotHookedError < Error
|
17
|
-
def
|
18
|
-
"Spy was not hooked."
|
17
|
+
def to_s
|
18
|
+
@mesg || "Spy was not hooked."
|
19
19
|
end
|
20
20
|
end
|
21
21
|
|
22
22
|
class NeverHookedError < Error
|
23
|
-
def
|
24
|
-
"Spy was never hooked."
|
23
|
+
def to_s
|
24
|
+
@mesg || "Spy was never hooked."
|
25
25
|
end
|
26
26
|
end
|
27
27
|
|
28
28
|
class NoSpyError < Error
|
29
|
-
def
|
30
|
-
"Spy could not be found"
|
29
|
+
def to_s
|
30
|
+
@mesg || "Spy could not be found"
|
31
31
|
end
|
32
32
|
end
|
33
33
|
end
|
data/lib/spy/mock.rb
ADDED
@@ -0,0 +1,122 @@
|
|
1
|
+
module Spy
|
2
|
+
# A Mock is an object that has all the same methods as the given class.
|
3
|
+
# Each method however will raise a NeverHookedError if it hasn't been stubbed.
|
4
|
+
# If you attempt to stub a method on the mock that doesn't exist on the
|
5
|
+
# original class it will raise an error.
|
6
|
+
module Mock
|
7
|
+
include Base
|
8
|
+
CLASSES_NOT_TO_OVERRIDE = [Enumerable, Numeric, Comparable, Class, Module, Object]
|
9
|
+
METHODS_NOT_TO_OVERRIDE = [:initialize, :method]
|
10
|
+
|
11
|
+
def initialize
|
12
|
+
end
|
13
|
+
|
14
|
+
def is_a?(other)
|
15
|
+
self.class.ancestors.include?(other)
|
16
|
+
end
|
17
|
+
|
18
|
+
alias :kind_of? :is_a?
|
19
|
+
|
20
|
+
def instance_of?(other)
|
21
|
+
other == self.class
|
22
|
+
end
|
23
|
+
|
24
|
+
def method(method_name)
|
25
|
+
new_method = super
|
26
|
+
if new_method.parameters.size >= 1 &&
|
27
|
+
new_method.parameters.last.last == :never_hooked
|
28
|
+
|
29
|
+
begin
|
30
|
+
_mock_class.send(:remove_method, method_name)
|
31
|
+
real_method = super
|
32
|
+
ensure
|
33
|
+
_mock_class.send(:define_method, method_name, new_method)
|
34
|
+
end
|
35
|
+
|
36
|
+
real_method
|
37
|
+
else
|
38
|
+
new_method
|
39
|
+
end
|
40
|
+
end
|
41
|
+
|
42
|
+
class << self
|
43
|
+
def new(klass)
|
44
|
+
mock_klass = Class.new(klass)
|
45
|
+
mock_klass.class_exec do
|
46
|
+
alias :_mock_class :class
|
47
|
+
private :_mock_class
|
48
|
+
|
49
|
+
define_method(:class) do
|
50
|
+
klass
|
51
|
+
end
|
52
|
+
|
53
|
+
include Mock
|
54
|
+
end
|
55
|
+
Agency.instance.recruit(mock_klass)
|
56
|
+
mock_klass
|
57
|
+
end
|
58
|
+
|
59
|
+
def included(mod)
|
60
|
+
method_classes = classes_to_override_methods(mod)
|
61
|
+
|
62
|
+
mocked_methods = []
|
63
|
+
[:public, :protected, :private].each do |visibility|
|
64
|
+
get_inherited_methods(method_classes, visibility).each do |method_name|
|
65
|
+
mocked_methods << method_name
|
66
|
+
args = args_for_method(mod.instance_method(method_name))
|
67
|
+
|
68
|
+
mod.class_eval <<-DEF_METHOD, __FILE__, __LINE__+1
|
69
|
+
def #{method_name}(#{args})
|
70
|
+
raise ::Spy::NeverHookedError, "'#{method_name}' was never hooked on mock spy."
|
71
|
+
end
|
72
|
+
|
73
|
+
#{visibility} :#{method_name}
|
74
|
+
DEF_METHOD
|
75
|
+
end
|
76
|
+
end
|
77
|
+
|
78
|
+
mod.define_singleton_method(:mocked_methods) do
|
79
|
+
mocked_methods
|
80
|
+
end
|
81
|
+
end
|
82
|
+
|
83
|
+
private
|
84
|
+
|
85
|
+
def classes_to_override_methods(mod)
|
86
|
+
method_classes = mod.ancestors
|
87
|
+
method_classes.shift
|
88
|
+
method_classes.delete(self)
|
89
|
+
CLASSES_NOT_TO_OVERRIDE.each do |klass|
|
90
|
+
index = method_classes.index(klass)
|
91
|
+
method_classes.slice!(index..-1) if index
|
92
|
+
end
|
93
|
+
method_classes
|
94
|
+
end
|
95
|
+
|
96
|
+
def get_inherited_methods(klass_ancestors, visibility)
|
97
|
+
instance_methods = klass_ancestors.map do |klass|
|
98
|
+
klass.send("#{visibility}_instance_methods".to_sym, false)
|
99
|
+
end
|
100
|
+
instance_methods.flatten!
|
101
|
+
instance_methods.uniq!
|
102
|
+
instance_methods - METHODS_NOT_TO_OVERRIDE
|
103
|
+
end
|
104
|
+
|
105
|
+
def args_for_method(method)
|
106
|
+
args = method.parameters.map do |type,name|
|
107
|
+
name ||= :args
|
108
|
+
case type
|
109
|
+
when :req
|
110
|
+
name
|
111
|
+
when :opt
|
112
|
+
"#{name} = nil"
|
113
|
+
when :rest
|
114
|
+
"*#{name}"
|
115
|
+
end
|
116
|
+
end.compact
|
117
|
+
args << "&never_hooked"
|
118
|
+
args.join(",")
|
119
|
+
end
|
120
|
+
end
|
121
|
+
end
|
122
|
+
end
|
data/lib/spy/subroutine.rb
CHANGED
@@ -1,5 +1,6 @@
|
|
1
1
|
module Spy
|
2
2
|
class Subroutine
|
3
|
+
include Base
|
3
4
|
# @!attribute [r] base_object
|
4
5
|
# @return [Object] the object that is being watched
|
5
6
|
#
|
@@ -45,7 +46,6 @@ module Spy
|
|
45
46
|
@hook_opts = opts
|
46
47
|
@original_method_visibility = method_visibility_of(method_name)
|
47
48
|
hook_opts[:visibility] ||= original_method_visibility
|
48
|
-
hook_opts[:force] ||= base_object.is_a?(Double)
|
49
49
|
|
50
50
|
if original_method_visibility || !hook_opts[:force]
|
51
51
|
@original_method = current_method
|
@@ -361,14 +361,11 @@ module Spy
|
|
361
361
|
end.compact
|
362
362
|
end
|
363
363
|
|
364
|
-
private
|
365
|
-
|
364
|
+
# @private
|
366
365
|
def get_spy_id(method)
|
367
366
|
return nil unless method.parameters[0].is_a?(Array)
|
368
|
-
|
369
|
-
if
|
370
|
-
first_param_name.split("_").last.to_i
|
371
|
-
end
|
367
|
+
id = method.parameters[0][1].to_s.sub!("__spy_args_", "")
|
368
|
+
id.to_i if id
|
372
369
|
end
|
373
370
|
end
|
374
371
|
end
|
data/lib/spy/version.rb
CHANGED
data/lib/spy.rb
CHANGED
@@ -1,9 +1,10 @@
|
|
1
1
|
require "spy/core_ext/marshal"
|
2
2
|
require "spy/agency"
|
3
|
+
require "spy/base"
|
3
4
|
require "spy/call_log"
|
4
5
|
require "spy/constant"
|
5
|
-
require "spy/double"
|
6
6
|
require "spy/exceptions"
|
7
|
+
require "spy/mock"
|
7
8
|
require "spy/nest"
|
8
9
|
require "spy/subroutine"
|
9
10
|
require "spy/version"
|
@@ -107,17 +108,31 @@ module Spy
|
|
107
108
|
spies.size > 1 ? spies : spies.first
|
108
109
|
end
|
109
110
|
|
111
|
+
def mock(klass, *stubs)
|
112
|
+
new_mock = Mock.new(klass).new
|
113
|
+
if stubs.size > 0
|
114
|
+
on(new_mock, *stubs)
|
115
|
+
end
|
116
|
+
new_mock
|
117
|
+
end
|
118
|
+
|
119
|
+
def mock_all(klass, *stubs)
|
120
|
+
mock_klass = Mock.new(klass)
|
121
|
+
new_mock = mock_klass.new
|
122
|
+
|
123
|
+
spies = stubs.size > 0 ? on(new_mock, *stubs) : []
|
124
|
+
|
125
|
+
unstubbed_methods = mock_klass.mocked_methods - spies.map(&:method_name)
|
126
|
+
on(new_mock, *unstubbed_methods) if unstubbed_methods.size > 0
|
127
|
+
|
128
|
+
new_mock
|
129
|
+
end
|
130
|
+
|
110
131
|
# unhook all methods
|
111
132
|
def teardown
|
112
133
|
Agency.instance.dissolve!
|
113
134
|
end
|
114
135
|
|
115
|
-
# returns a double
|
116
|
-
# (see Double#initizalize)
|
117
|
-
def double(*args)
|
118
|
-
Double.new(*args)
|
119
|
-
end
|
120
|
-
|
121
136
|
# retrieve the spy from an object
|
122
137
|
# @param base_object
|
123
138
|
# @param method_names *[Symbol]
|
@@ -16,14 +16,9 @@ class TestSubClass < TestClass
|
|
16
16
|
P = :p
|
17
17
|
end
|
18
18
|
|
19
|
-
require "rspec/mocks/mutate_const"
|
20
|
-
|
21
19
|
module Spy
|
22
20
|
describe "Constant Mutating" do
|
23
21
|
|
24
|
-
require "rspec/mocks/mutate_const"
|
25
|
-
include RSpec::Mocks::RecursiveConstMethods
|
26
|
-
|
27
22
|
def reset_rspec_mocks
|
28
23
|
Spy.teardown
|
29
24
|
end
|
@@ -360,9 +355,6 @@ module Spy
|
|
360
355
|
end
|
361
356
|
|
362
357
|
describe Constant do
|
363
|
-
require "rspec/mocks/mutate_const"
|
364
|
-
include RSpec::Mocks::RecursiveConstMethods
|
365
|
-
|
366
358
|
def reset_rspec_mocks
|
367
359
|
Spy.teardown
|
368
360
|
end
|
@@ -0,0 +1,25 @@
|
|
1
|
+
require 'test_helper'
|
2
|
+
|
3
|
+
class TestMocking < MiniTest::Unit::TestCase
|
4
|
+
def teardown
|
5
|
+
Spy::Agency.instance.dissolve!
|
6
|
+
end
|
7
|
+
|
8
|
+
def test_spy_on_mock_does_not_raise
|
9
|
+
mock = Spy.mock(Pen)
|
10
|
+
spy = Spy.on(mock, :write).and_return(:awesome)
|
11
|
+
assert_equal :awesome, mock.write("hello")
|
12
|
+
assert spy.has_been_called?
|
13
|
+
end
|
14
|
+
|
15
|
+
def test_spy_mock_shortcuts
|
16
|
+
mock = Spy.mock(Pen, :another, write_hello: :goodbye)
|
17
|
+
assert_nil mock.another
|
18
|
+
assert_equal :goodbye, mock.write_hello
|
19
|
+
end
|
20
|
+
|
21
|
+
def test_spy_mock_all
|
22
|
+
mock = Spy.mock_all(Pen)
|
23
|
+
assert_nil mock.another
|
24
|
+
end
|
25
|
+
end
|
@@ -0,0 +1,88 @@
|
|
1
|
+
require 'test_helper'
|
2
|
+
|
3
|
+
module Spy
|
4
|
+
class TestMock < MiniTest::Unit::TestCase
|
5
|
+
class BluePen < Pen
|
6
|
+
def write_hello(other)
|
7
|
+
end
|
8
|
+
end
|
9
|
+
|
10
|
+
def setup
|
11
|
+
@pen_mock = Mock.new(BluePen)
|
12
|
+
@pen = @pen_mock.new
|
13
|
+
end
|
14
|
+
|
15
|
+
def teardown
|
16
|
+
Spy::Agency.instance.dissolve!
|
17
|
+
end
|
18
|
+
|
19
|
+
def test_class_methods
|
20
|
+
assert @pen.kind_of?(BluePen)
|
21
|
+
assert @pen.kind_of?(Pen)
|
22
|
+
assert @pen.is_a?(Pen)
|
23
|
+
assert @pen.is_a?(BluePen)
|
24
|
+
assert @pen.instance_of?(BluePen)
|
25
|
+
assert_equal BluePen, @pen.class
|
26
|
+
end
|
27
|
+
|
28
|
+
def test_raises_error_on_unstubbed_method
|
29
|
+
assert_raises Spy::NeverHookedError do
|
30
|
+
@pen.write("")
|
31
|
+
end
|
32
|
+
end
|
33
|
+
|
34
|
+
def test_mimics_visibility
|
35
|
+
assert @pen.singleton_class.public_method_defined? :public_method
|
36
|
+
assert @pen.singleton_class.protected_method_defined? :protected_method
|
37
|
+
assert @pen.singleton_class.private_method_defined? :private_method
|
38
|
+
end
|
39
|
+
|
40
|
+
def test_that_method_spy_keeps_arity
|
41
|
+
assert_raises ArgumentError do
|
42
|
+
@pen.write
|
43
|
+
end
|
44
|
+
|
45
|
+
assert_raises ArgumentError do
|
46
|
+
@pen.write("hello", "world")
|
47
|
+
end
|
48
|
+
|
49
|
+
assert_raises ArgumentError do
|
50
|
+
@pen.write_hello
|
51
|
+
end
|
52
|
+
|
53
|
+
assert_raises ArgumentError do
|
54
|
+
@pen.greet
|
55
|
+
end
|
56
|
+
|
57
|
+
assert_raises ArgumentError do
|
58
|
+
@pen.greet("hello", "bob", "error")
|
59
|
+
end
|
60
|
+
end
|
61
|
+
|
62
|
+
def test_that_and_call_original_works
|
63
|
+
Spy.on(@pen, :another).and_call_through
|
64
|
+
assert_equal "another", @pen.another
|
65
|
+
Spy.off(@pen, :another)
|
66
|
+
assert_raises Spy::NeverHookedError do
|
67
|
+
@pen.another
|
68
|
+
end
|
69
|
+
end
|
70
|
+
|
71
|
+
def test_mocked_methods
|
72
|
+
pen_methods = Pen.public_instance_methods(false) +
|
73
|
+
Pen.protected_instance_methods(false) +
|
74
|
+
Pen.private_instance_methods(false)
|
75
|
+
pen_methods.delete(:initialize)
|
76
|
+
assert_equal pen_methods.sort, @pen_mock.mocked_methods.sort
|
77
|
+
end
|
78
|
+
|
79
|
+
def test_base_class_methods_are_not_stubbed
|
80
|
+
(Object.instance_methods - [:tap, :pretty_print_inspect]).each do |method_name|
|
81
|
+
object_method = Object.instance_method(method_name)
|
82
|
+
if object_method.parameters == []
|
83
|
+
@pen.send(method_name)
|
84
|
+
end
|
85
|
+
end
|
86
|
+
end
|
87
|
+
end
|
88
|
+
end
|
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.
|
4
|
+
version: 0.3.0
|
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-03-
|
12
|
+
date: 2013-03-12 00:00:00.000000000 Z
|
13
13
|
dependencies:
|
14
14
|
- !ruby/object:Gem::Dependency
|
15
15
|
name: pry
|
@@ -124,11 +124,12 @@ files:
|
|
124
124
|
- Rakefile
|
125
125
|
- lib/spy.rb
|
126
126
|
- lib/spy/agency.rb
|
127
|
+
- lib/spy/base.rb
|
127
128
|
- lib/spy/call_log.rb
|
128
129
|
- lib/spy/constant.rb
|
129
130
|
- lib/spy/core_ext/marshal.rb
|
130
|
-
- lib/spy/double.rb
|
131
131
|
- lib/spy/exceptions.rb
|
132
|
+
- lib/spy/mock.rb
|
132
133
|
- lib/spy/nest.rb
|
133
134
|
- lib/spy/subroutine.rb
|
134
135
|
- lib/spy/version.rb
|
@@ -138,7 +139,6 @@ files:
|
|
138
139
|
- spec/spy/any_instance_spec.rb
|
139
140
|
- spec/spy/hash_excluding_matcher_spec.rb
|
140
141
|
- spec/spy/hash_including_matcher_spec.rb
|
141
|
-
- spec/spy/mock_spec.rb
|
142
142
|
- spec/spy/mutate_const_spec.rb
|
143
143
|
- spec/spy/nil_expectation_warning_spec.rb
|
144
144
|
- spec/spy/null_object_mock_spec.rb
|
@@ -152,8 +152,9 @@ files:
|
|
152
152
|
- spy.gemspec
|
153
153
|
- test/integration/test_constant_spying.rb
|
154
154
|
- test/integration/test_instance_method.rb
|
155
|
+
- test/integration/test_mocking.rb
|
155
156
|
- test/integration/test_subroutine_spying.rb
|
156
|
-
- test/spy/
|
157
|
+
- test/spy/test_mock.rb
|
157
158
|
- test/spy/test_subroutine.rb
|
158
159
|
- test/support/pen.rb
|
159
160
|
- test/test_helper.rb
|
@@ -191,7 +192,6 @@ test_files:
|
|
191
192
|
- spec/spy/any_instance_spec.rb
|
192
193
|
- spec/spy/hash_excluding_matcher_spec.rb
|
193
194
|
- spec/spy/hash_including_matcher_spec.rb
|
194
|
-
- spec/spy/mock_spec.rb
|
195
195
|
- spec/spy/mutate_const_spec.rb
|
196
196
|
- spec/spy/nil_expectation_warning_spec.rb
|
197
197
|
- spec/spy/null_object_mock_spec.rb
|
@@ -204,8 +204,9 @@ test_files:
|
|
204
204
|
- spec/spy/to_ary_spec.rb
|
205
205
|
- test/integration/test_constant_spying.rb
|
206
206
|
- test/integration/test_instance_method.rb
|
207
|
+
- test/integration/test_mocking.rb
|
207
208
|
- test/integration/test_subroutine_spying.rb
|
208
|
-
- test/spy/
|
209
|
+
- test/spy/test_mock.rb
|
209
210
|
- test/spy/test_subroutine.rb
|
210
211
|
- test/support/pen.rb
|
211
212
|
- test/test_helper.rb
|
data/lib/spy/double.rb
DELETED
@@ -1,28 +0,0 @@
|
|
1
|
-
module Spy
|
2
|
-
class Double
|
3
|
-
def initialize(name, *args)
|
4
|
-
@name = name
|
5
|
-
|
6
|
-
if args.size > 0
|
7
|
-
Spy.on(self,*args)
|
8
|
-
end
|
9
|
-
end
|
10
|
-
|
11
|
-
# @private
|
12
|
-
def ==(other)
|
13
|
-
other == self
|
14
|
-
end
|
15
|
-
|
16
|
-
# @private
|
17
|
-
def inspect
|
18
|
-
"#<#{self.class}:#{sprintf '0x%x', self.object_id} @name=#{@name.inspect}>"
|
19
|
-
end
|
20
|
-
|
21
|
-
# @private
|
22
|
-
def to_s
|
23
|
-
inspect.gsub('<','[').gsub('>',']')
|
24
|
-
end
|
25
|
-
|
26
|
-
alias_method :to_str, :to_s
|
27
|
-
end
|
28
|
-
end
|
data/spec/spy/mock_spec.rb
DELETED
@@ -1,222 +0,0 @@
|
|
1
|
-
require 'spec_helper'
|
2
|
-
|
3
|
-
module Spy
|
4
|
-
describe Double do
|
5
|
-
before(:each) { @double = Spy.double("test double") }
|
6
|
-
|
7
|
-
it "has method_missing as private" do
|
8
|
-
expect(Spy.double("stuff").private_instance_methods).to include_method(:method_missing)
|
9
|
-
end
|
10
|
-
|
11
|
-
it "does not respond_to? method_missing (because it's private)" do
|
12
|
-
expect(Spy.double("stuff")).not_to respond_to(:method_missing)
|
13
|
-
end
|
14
|
-
|
15
|
-
it "fails when receiving message specified as not to be received" do
|
16
|
-
spy = Spy.on(@double, :not_expected)
|
17
|
-
expect(spy).to_not have_been_called
|
18
|
-
end
|
19
|
-
|
20
|
-
it "fails if unexpected method called" do
|
21
|
-
expect {
|
22
|
-
@double.something("a","b","c")
|
23
|
-
}.to raise_error
|
24
|
-
end
|
25
|
-
|
26
|
-
it "uses block for expectation if provided" do
|
27
|
-
spy = Spy.on(@double, :something).and_return do | a, b |
|
28
|
-
expect(a).to eq "a"
|
29
|
-
expect(b).to eq "b"
|
30
|
-
"booh"
|
31
|
-
end
|
32
|
-
expect(@double.something("a", "b")).to eq "booh"
|
33
|
-
expect(spy).to have_been_called
|
34
|
-
end
|
35
|
-
|
36
|
-
it "fails if expectation block fails" do
|
37
|
-
Spy.on(@double, :something).and_return do | bool|
|
38
|
-
expect(bool).to be_true
|
39
|
-
end
|
40
|
-
|
41
|
-
expect {
|
42
|
-
@double.something false
|
43
|
-
}.to raise_error(RSpec::Expectations::ExpectationNotMetError)
|
44
|
-
end
|
45
|
-
|
46
|
-
it "passes proc to expectation block without an argument" do
|
47
|
-
spy = Spy.on(@double, :foo).and_return do |&block|
|
48
|
-
expect(block.call).to eq(:bar)
|
49
|
-
end
|
50
|
-
@double.foo { :bar }
|
51
|
-
expect(spy).to have_been_called
|
52
|
-
end
|
53
|
-
|
54
|
-
it "passes proc to expectation block with an argument" do
|
55
|
-
spy = Spy.on(@double, :foo).and_return do |arg, &block|
|
56
|
-
expect(block.call).to eq(:bar)
|
57
|
-
end
|
58
|
-
@double.foo(:arg) { :bar }
|
59
|
-
expect(spy).to have_been_called
|
60
|
-
end
|
61
|
-
|
62
|
-
it "passes proc to stub block without an argurment" do
|
63
|
-
spy = Spy.on(@double, :foo).and_return do |&block|
|
64
|
-
expect(block.call).to eq(:bar)
|
65
|
-
end
|
66
|
-
@double.foo { :bar }
|
67
|
-
expect(spy).to have_been_called
|
68
|
-
end
|
69
|
-
|
70
|
-
it "passes proc to stub block with an argument" do
|
71
|
-
spy = Spy.on(@double, :foo) do |arg, &block|
|
72
|
-
expect(block.call).to eq(:bar)
|
73
|
-
end
|
74
|
-
@double.foo(:arg) { :bar }
|
75
|
-
expect(spy).to have_been_called
|
76
|
-
end
|
77
|
-
|
78
|
-
it "fails right away when method defined as never is received" do
|
79
|
-
Spy.on(@double, :not_expected).never
|
80
|
-
expect { @double.not_expected }.
|
81
|
-
to raise_error(RSpec::Mocks::MockExpectationError,
|
82
|
-
%Q|(Double "test double").not_expected(no args)\n expected: 0 times\n received: 1 time|
|
83
|
-
)
|
84
|
-
end
|
85
|
-
|
86
|
-
it "raises RuntimeError by default" do
|
87
|
-
Spy.on(@double, :something).and_raise
|
88
|
-
expect { @double.something }.to raise_error(RuntimeError)
|
89
|
-
end
|
90
|
-
|
91
|
-
it "raises RuntimeError with a message by default" do
|
92
|
-
Spy.on(@double, :something).and_raise("error message")
|
93
|
-
expect { @double.something }.to raise_error(RuntimeError, "error message")
|
94
|
-
end
|
95
|
-
|
96
|
-
it "raises an exception of a given type without an error message" do
|
97
|
-
Spy.on(@double, :something).and_raise(StandardError)
|
98
|
-
expect { @double.something }.to raise_error(StandardError)
|
99
|
-
end
|
100
|
-
|
101
|
-
it "raises an exception of a given type with a message" do
|
102
|
-
Spy.on(@double, :something).and_raise(RuntimeError, "error message")
|
103
|
-
expect { @double.something }.to raise_error(RuntimeError, "error message")
|
104
|
-
end
|
105
|
-
|
106
|
-
it "raises a given instance of an exception" do
|
107
|
-
Spy.on(@double, :something).and_raise(RuntimeError.new("error message"))
|
108
|
-
expect { @double.something }.to raise_error(RuntimeError, "error message")
|
109
|
-
end
|
110
|
-
|
111
|
-
class OutOfGas < StandardError
|
112
|
-
attr_reader :amount, :units
|
113
|
-
def initialize(amount, units)
|
114
|
-
@amount = amount
|
115
|
-
@units = units
|
116
|
-
end
|
117
|
-
end
|
118
|
-
|
119
|
-
it "raises a given instance of an exception with arguments other than the standard 'message'" do
|
120
|
-
Spy.on(@double, :something).and_raise(OutOfGas.new(2, :oz))
|
121
|
-
|
122
|
-
begin
|
123
|
-
@double.something
|
124
|
-
fail "OutOfGas was not raised"
|
125
|
-
rescue OutOfGas => e
|
126
|
-
expect(e.amount).to eq 2
|
127
|
-
expect(e.units).to eq :oz
|
128
|
-
end
|
129
|
-
end
|
130
|
-
|
131
|
-
it "throws when told to" do
|
132
|
-
Spy.on(@double, :something).and_throw(:blech)
|
133
|
-
expect {
|
134
|
-
@double.something
|
135
|
-
}.to throw_symbol(:blech)
|
136
|
-
end
|
137
|
-
|
138
|
-
it "returns value from block by default" do
|
139
|
-
spy = Spy.on(@double, :method_that_yields).and_yield
|
140
|
-
value = @double.method_that_yields { :returned_obj }
|
141
|
-
expect(value).to eq :returned_obj
|
142
|
-
expect(spy).to have_been_called
|
143
|
-
end
|
144
|
-
|
145
|
-
it "is able to raise from method calling yielding double" do
|
146
|
-
spy = Spy.on(@double, :yield_me).and_yield 44
|
147
|
-
|
148
|
-
expect {
|
149
|
-
@double.yield_me do |x|
|
150
|
-
raise "Bang"
|
151
|
-
end
|
152
|
-
}.to raise_error(StandardError, "Bang")
|
153
|
-
|
154
|
-
expect(spy).to have_been_called
|
155
|
-
end
|
156
|
-
|
157
|
-
it "assigns stub return values" do
|
158
|
-
double = Spy.double('name', :message => :response)
|
159
|
-
expect(double.message).to eq :response
|
160
|
-
end
|
161
|
-
|
162
|
-
end
|
163
|
-
|
164
|
-
describe "a double message receiving a block" do
|
165
|
-
before(:each) do
|
166
|
-
@double = Spy.double("double")
|
167
|
-
@calls = 0
|
168
|
-
end
|
169
|
-
|
170
|
-
def add_call
|
171
|
-
@calls = @calls + 1
|
172
|
-
end
|
173
|
-
|
174
|
-
it "calls the block after #should_receive" do
|
175
|
-
spy = Spy.on(@double, :foo).and_return { add_call }
|
176
|
-
|
177
|
-
@double.foo
|
178
|
-
|
179
|
-
expect(@calls).to eq 1
|
180
|
-
expect(spy).to have_been_called
|
181
|
-
end
|
182
|
-
end
|
183
|
-
|
184
|
-
describe 'string representation generated by #to_s' do
|
185
|
-
it 'does not contain < because that might lead to invalid HTML in some situations' do
|
186
|
-
double = Spy.double("Dog")
|
187
|
-
valid_html_str = "#{double}"
|
188
|
-
expect(valid_html_str).not_to include('<')
|
189
|
-
end
|
190
|
-
end
|
191
|
-
|
192
|
-
describe "string representation generated by #to_str" do
|
193
|
-
it "looks the same as #to_s" do
|
194
|
-
double = Spy.double("Foo")
|
195
|
-
expect(double.to_str).to eq double.to_s
|
196
|
-
end
|
197
|
-
end
|
198
|
-
|
199
|
-
describe "double created with no name" do
|
200
|
-
it "does not use a name in a failure message" do
|
201
|
-
double = Spy.double()
|
202
|
-
expect {double.foo}.to raise_error(/Double received/)
|
203
|
-
end
|
204
|
-
|
205
|
-
it "does respond to initially stubbed methods" do
|
206
|
-
double = Spy.double("name", :foo => "woo", :bar => "car")
|
207
|
-
expect(double.foo).to eq "woo"
|
208
|
-
expect(double.bar).to eq "car"
|
209
|
-
end
|
210
|
-
end
|
211
|
-
|
212
|
-
describe "==" do
|
213
|
-
it "sends '== self' to the comparison object" do
|
214
|
-
first = Spy.double('first')
|
215
|
-
second = Spy.double('second')
|
216
|
-
|
217
|
-
spy = Spy.on(first, :==)
|
218
|
-
second == first
|
219
|
-
expect(spy.calls.first.args).to eq([second])
|
220
|
-
end
|
221
|
-
end
|
222
|
-
end
|
data/test/spy/test_double.rb
DELETED
@@ -1,23 +0,0 @@
|
|
1
|
-
require 'test_helper'
|
2
|
-
|
3
|
-
module Spy
|
4
|
-
class TestDouble < MiniTest::Unit::TestCase
|
5
|
-
def teardown
|
6
|
-
Spy::Agency.instance.dissolve!
|
7
|
-
end
|
8
|
-
|
9
|
-
def test_double_creation
|
10
|
-
double = Double.new("NewDouble", :meth_1, :meth_2)
|
11
|
-
|
12
|
-
assert_nil double.meth_1
|
13
|
-
assert_nil double.meth_2
|
14
|
-
end
|
15
|
-
|
16
|
-
def test_double_hash_input
|
17
|
-
double = Double.new("NewDouble", meth_1: :meth_1, meth_2: :meth_2)
|
18
|
-
|
19
|
-
assert_equal :meth_1, double.meth_1
|
20
|
-
assert_equal :meth_2, double.meth_2
|
21
|
-
end
|
22
|
-
end
|
23
|
-
end
|