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