muack 0.5.2 → 0.7.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.
- checksums.yaml +4 -4
- data/CHANGES.md +42 -0
- data/README.md +49 -8
- data/lib/muack.rb +6 -10
- data/lib/muack/any_instance_of.rb +5 -1
- data/lib/muack/definition.rb +2 -1
- data/lib/muack/error.rb +18 -0
- data/lib/muack/mock.rb +40 -18
- data/lib/muack/modifier.rb +12 -1
- data/lib/muack/session.rb +7 -7
- data/lib/muack/spy.rb +23 -0
- data/lib/muack/stub.rb +5 -6
- data/lib/muack/version.rb +1 -1
- data/muack.gemspec +4 -3
- data/test/test_any_instance_of.rb +52 -13
- data/test/test_mock.rb +7 -7
- data/test/test_proxy.rb +28 -16
- data/test/test_satisfy.rb +6 -6
- data/test/test_stub.rb +92 -1
- metadata +4 -3
- data/lib/muack/proxy.rb +0 -39
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: f7727020cca0c41dfd846c0b7628aec71c2cbb59
|
4
|
+
data.tar.gz: 9fb966eb9e9e1dc65d8f0e938d2d0a619813f204
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 669fbb6e488468cf698bf28782e989f70a7fa82c447b91ef4efdc62892add2bf828d9604d682fbe4b13780cf1599017fbfff22f5d7fa8bbb43c0603b7e5b5d97
|
7
|
+
data.tar.gz: b6465725ab6109aeead47a3bba111df9095434ec557017ad0141c1ff441e51054504f4217d19fc6b9e1ad2ef2e86e6fdd8cc59cc3568d2498b89bdd30e257dd4
|
data/CHANGES.md
CHANGED
@@ -1,5 +1,47 @@
|
|
1
1
|
# CHANGES
|
2
2
|
|
3
|
+
## Muack 0.7.0 -- 2013-06-27
|
4
|
+
|
5
|
+
### Incompatible changes
|
6
|
+
|
7
|
+
* Now instead of using mock_proxy, we use `proxy` as a modifier. That says
|
8
|
+
mock_proxy and stub_proxy no longer existed. like this:
|
9
|
+
|
10
|
+
``` ruby
|
11
|
+
mock(object).to_s.proxy
|
12
|
+
stub(object).to_s.proxy
|
13
|
+
```
|
14
|
+
|
15
|
+
We change this due to the introduction of spies.
|
16
|
+
|
17
|
+
### Enhancement
|
18
|
+
|
19
|
+
* We have spies support now. Here's an example:
|
20
|
+
|
21
|
+
``` ruby
|
22
|
+
subject = Object.new
|
23
|
+
stub(subject).foo(1)
|
24
|
+
subject.foo(1)
|
25
|
+
|
26
|
+
spy(subject).foo(1)
|
27
|
+
spy(subject).bar # This doesn't verify immediately.
|
28
|
+
Muack.verify # This fails, saying `bar` was never called.
|
29
|
+
```
|
30
|
+
|
31
|
+
* It would now raise a `StubHasNoTimes` exception if you tried to set times
|
32
|
+
on stubs, which has no meanings in Muack. Use `mock` or `spy` instead if
|
33
|
+
you need to specify times.
|
34
|
+
|
35
|
+
* Muack.reset and Muack.verify is now thread-safe.
|
36
|
+
You can run test cases concurrently now.
|
37
|
+
|
38
|
+
* AnyInstanceOf now has a more readable inspect.
|
39
|
+
* Improved various error messages. e.g. CannotFindInjectionName.
|
40
|
+
* You can now set ENV['MUACK_RECURSION_LEVEL'] to raise the limit
|
41
|
+
to find a new method name when we're injecting a method. Normally
|
42
|
+
this should not happen, and it could be a bug in Muack. But instead of
|
43
|
+
putting a magic number 9 out there as before, this might be better.
|
44
|
+
|
3
45
|
## Muack 0.5.2 -- 2013-06-26
|
4
46
|
|
5
47
|
* Add `returns` modifier which you can pass the return values if passing
|
data/README.md
CHANGED
@@ -101,27 +101,47 @@ User.find('42').times(0)
|
|
101
101
|
User.find('42') # raises a Muack::Unexpected
|
102
102
|
```
|
103
103
|
|
104
|
-
#### [
|
104
|
+
#### [proxy](https://github.com/rr/rr/blob/e4b4907fd0488738affb4dab8ce88cbe9fa6580e/doc/03_api_overview.md#mockproxy)
|
105
105
|
|
106
|
-
|
107
|
-
|
106
|
+
Instead of calling `proxy` immediately after calling `mock`, we put
|
107
|
+
`proxy` the last because it's a method from `Muack::Modifier`.
|
108
108
|
|
109
109
|
``` ruby
|
110
110
|
view = controller.template
|
111
|
-
|
112
|
-
|
111
|
+
mock(view).render(:partial => "right_navigation").proxy
|
112
|
+
mock(view).render(:partial => "user_info") do |html|
|
113
|
+
html.should include("John Doe")
|
114
|
+
"Different html"
|
115
|
+
end.proxy
|
116
|
+
```
|
117
|
+
|
118
|
+
If you feel it is weird to put proxy the last, you can also use
|
119
|
+
`returns` modifier to put the block last as this:
|
120
|
+
|
121
|
+
``` ruby
|
122
|
+
view = controller.template
|
123
|
+
mock(view).render(:partial => "right_navigation").proxy
|
124
|
+
mock(view).render(:partial => "user_info").proxy.returns do |html|
|
113
125
|
html.should include("John Doe")
|
114
126
|
"Different html"
|
115
127
|
end
|
116
128
|
```
|
117
129
|
|
118
|
-
|
130
|
+
The same goes to `stub`.
|
119
131
|
|
120
|
-
|
132
|
+
``` ruby
|
133
|
+
view = controller.template
|
134
|
+
stub(view).render(:partial => "user_info") do |html|
|
135
|
+
html.should include("Joe Smith")
|
136
|
+
html
|
137
|
+
end.proxy
|
138
|
+
```
|
139
|
+
|
140
|
+
Or use `returns`:
|
121
141
|
|
122
142
|
``` ruby
|
123
143
|
view = controller.template
|
124
|
-
|
144
|
+
stub(view).render(:partial => "user_info").proxy.returns do |html|
|
125
145
|
html.should include("Joe Smith")
|
126
146
|
html
|
127
147
|
end
|
@@ -141,6 +161,27 @@ any_instance_of(User) do |u|
|
|
141
161
|
end
|
142
162
|
```
|
143
163
|
|
164
|
+
#### [Spies](https://github.com/rr/rr/blob/e4b4907fd0488738affb4dab8ce88cbe9fa6580e/doc/03_api_overview.md#spies)
|
165
|
+
|
166
|
+
We don't try to provide different methods for different testing framework,
|
167
|
+
so that we don't have to create so many testing framework adapters, and
|
168
|
+
try to be smart to find the correct adapter. There are simply too many
|
169
|
+
testing frameworks out there. Ruby's built-in test/unit and minitest have
|
170
|
+
a lot of different versions, so does rspec.
|
171
|
+
|
172
|
+
Here we just try to do it the Muack's way:
|
173
|
+
|
174
|
+
|
175
|
+
``` ruby
|
176
|
+
subject = Object.new
|
177
|
+
stub(subject).foo(1)
|
178
|
+
subject.foo(1)
|
179
|
+
|
180
|
+
spy(subject).foo(1)
|
181
|
+
spy(subject).bar # This doesn't verify immediately.
|
182
|
+
Muack.verify # This fails, saying `bar` was never called.
|
183
|
+
```
|
184
|
+
|
144
185
|
#### [Block form](https://github.com/rr/rr/blob/e4b4907fd0488738affb4dab8ce88cbe9fa6580e/doc/03_api_overview.md#block-syntax)
|
145
186
|
|
146
187
|
Block form is also supported. However we don't support `instance_eval` form.
|
data/lib/muack.rb
CHANGED
@@ -10,12 +10,13 @@ module Muack
|
|
10
10
|
end
|
11
11
|
|
12
12
|
def self.session
|
13
|
-
|
13
|
+
Thread.current[:muack_session] ||= Muack::Session.new
|
14
14
|
end
|
15
15
|
|
16
16
|
def self.reset
|
17
|
-
|
18
|
-
|
17
|
+
session = Thread.current[:muack_session]
|
18
|
+
session && session.reset
|
19
|
+
Thread.current[:muack_session] = nil
|
19
20
|
end
|
20
21
|
|
21
22
|
module API
|
@@ -30,13 +31,8 @@ module Muack
|
|
30
31
|
if block_given? then yield(ret) else ret end
|
31
32
|
end
|
32
33
|
|
33
|
-
def
|
34
|
-
ret = Muack.session.
|
35
|
-
if block_given? then yield(ret) else ret end
|
36
|
-
end
|
37
|
-
|
38
|
-
def stub_proxy obj=Object.new
|
39
|
-
ret = Muack.session.stub_proxy(obj)
|
34
|
+
def spy obj
|
35
|
+
ret = Muack.session.spy(obj)
|
40
36
|
if block_given? then yield(ret) else ret end
|
41
37
|
end
|
42
38
|
|
data/lib/muack/definition.rb
CHANGED
data/lib/muack/error.rb
ADDED
@@ -0,0 +1,18 @@
|
|
1
|
+
|
2
|
+
module Muack
|
3
|
+
Error = Class.new(NotImplementedError)
|
4
|
+
class CannotFindInjectionName < Error
|
5
|
+
def initialize t, msg
|
6
|
+
super "\nCan't find a new method name for :#{msg}, tried #{t} times." \
|
7
|
+
"\nSet ENV['MUACK_RECURSION_LEVEL'] to raise this limit."
|
8
|
+
end
|
9
|
+
end
|
10
|
+
|
11
|
+
class StubHasNoTimes < Error
|
12
|
+
def initialize obj, defi, times
|
13
|
+
super "\nUse mocks if you want to specify times.\ne.g. " \
|
14
|
+
"mock(#{obj.inspect}).#{defi.msg}(#{defi.args.join(', ')})" \
|
15
|
+
".times(#{times})"
|
16
|
+
end
|
17
|
+
end
|
18
|
+
end
|
data/lib/muack/mock.rb
CHANGED
@@ -2,6 +2,7 @@
|
|
2
2
|
require 'muack/definition'
|
3
3
|
require 'muack/modifier'
|
4
4
|
require 'muack/failure'
|
5
|
+
require 'muack/error'
|
5
6
|
|
6
7
|
module Muack
|
7
8
|
class Mock < BasicObject
|
@@ -16,7 +17,7 @@ module Muack
|
|
16
17
|
|
17
18
|
# Public API: Bacon needs this, or we often ended up with stack overflow
|
18
19
|
def inspect
|
19
|
-
"
|
20
|
+
"Muack::API.#{__mock_class.name[/\w+$/].downcase}(#{object.inspect})"
|
20
21
|
end
|
21
22
|
|
22
23
|
# Public API: Define mocked method
|
@@ -41,12 +42,17 @@ module Muack
|
|
41
42
|
__mock_defis[defi.msg].pop
|
42
43
|
end
|
43
44
|
|
45
|
+
# used for Muack::Modifier#times to determine if it's a mock or not
|
46
|
+
def __mock_class
|
47
|
+
(class << self; self; end).superclass
|
48
|
+
end
|
49
|
+
|
44
50
|
# used for mocked object to dispatch mocked method
|
45
|
-
def __mock_dispatch msg, actual_args
|
51
|
+
def __mock_dispatch msg, actual_args
|
46
52
|
if defi = __mock_defis[msg].shift
|
47
53
|
__mock_disps_push(defi)
|
48
54
|
if __mock_check_args(defi.args, actual_args)
|
49
|
-
|
55
|
+
defi
|
50
56
|
else
|
51
57
|
Mock.__send__(:raise, # Wrong argument
|
52
58
|
Unexpected.new(object, [defi], msg, actual_args))
|
@@ -63,6 +69,25 @@ module Muack
|
|
63
69
|
end
|
64
70
|
end
|
65
71
|
|
72
|
+
# used for mocked object to dispatch mocked method
|
73
|
+
def __mock_dispatch_call disp, actual_args, actual_block
|
74
|
+
if disp.proxy
|
75
|
+
value = yield # need the original context for proxy or AnyInstanceOf
|
76
|
+
if disp.block
|
77
|
+
disp.block.call(value)
|
78
|
+
else
|
79
|
+
value
|
80
|
+
end
|
81
|
+
elsif block = disp.block
|
82
|
+
arity = block.arity
|
83
|
+
if arity < 0
|
84
|
+
block.call(*actual_args , &actual_block)
|
85
|
+
else
|
86
|
+
block.call(*actual_args.first(arity), &actual_block)
|
87
|
+
end
|
88
|
+
end
|
89
|
+
end
|
90
|
+
|
66
91
|
# used for Muack::Session#verify
|
67
92
|
def __mock_verify
|
68
93
|
__mock_defis.values.all?(&:empty?) || begin
|
@@ -94,7 +119,7 @@ module Muack
|
|
94
119
|
private
|
95
120
|
def __mock_inject_method defi
|
96
121
|
__mock_injected[defi.msg] = defi
|
97
|
-
target = object.singleton_class
|
122
|
+
target = object.singleton_class # would be the class in AnyInstanceOf
|
98
123
|
Mock.store_original_method(target, defi)
|
99
124
|
__mock_inject_mock_method(target, defi)
|
100
125
|
end
|
@@ -108,8 +133,9 @@ module Muack
|
|
108
133
|
end
|
109
134
|
|
110
135
|
def self.find_new_name klass, message, level=0
|
111
|
-
|
112
|
-
level
|
136
|
+
if level >= (::ENV['MUACK_RECURSION_LEVEL'] || 9).to_i
|
137
|
+
raise CannotFindInjectionName.new(level+1, message)
|
138
|
+
end
|
113
139
|
|
114
140
|
new_name = "__muack_mock_#{level}_#{message}".to_sym
|
115
141
|
if klass.instance_methods(false).include?(new_name)
|
@@ -122,21 +148,17 @@ module Muack
|
|
122
148
|
def __mock_inject_mock_method target, defi
|
123
149
|
mock = self # remember the context
|
124
150
|
target.__send__(:define_method, defi.msg){|*actual_args, &actual_block|
|
125
|
-
mock.__mock_dispatch(defi.msg, actual_args
|
151
|
+
disp = mock.__mock_dispatch(defi.msg, actual_args)
|
152
|
+
mock.__mock_dispatch_call(disp, actual_args, actual_block){
|
153
|
+
if disp.original_method
|
154
|
+
__send__(disp.original_method, *actual_args, &actual_block)
|
155
|
+
else
|
156
|
+
super(*actual_args, &actual_block)
|
157
|
+
end
|
158
|
+
}
|
126
159
|
}
|
127
160
|
end
|
128
161
|
|
129
|
-
def __mock_block_call defi, actual_args, actual_block
|
130
|
-
if block = defi.block
|
131
|
-
arity = block.arity
|
132
|
-
if arity < 0
|
133
|
-
block.call(*actual_args , &actual_block)
|
134
|
-
else
|
135
|
-
block.call(*actual_args.first(arity), &actual_block)
|
136
|
-
end
|
137
|
-
end
|
138
|
-
end
|
139
|
-
|
140
162
|
def __mock_check_args expected_args, actual_args
|
141
163
|
if expected_args == [WithAnyArgs]
|
142
164
|
true
|
data/lib/muack/modifier.rb
CHANGED
@@ -1,5 +1,5 @@
|
|
1
1
|
|
2
|
-
require 'muack/
|
2
|
+
require 'muack/error'
|
3
3
|
|
4
4
|
module Muack
|
5
5
|
class Modifier < Struct.new(:mock, :defi)
|
@@ -12,10 +12,21 @@ module Muack
|
|
12
12
|
# Public API
|
13
13
|
def returns val=nil, &block
|
14
14
|
defi.block = block || lambda{ val }
|
15
|
+
self
|
16
|
+
end
|
17
|
+
|
18
|
+
# Public API
|
19
|
+
def proxy
|
20
|
+
defi.proxy = true
|
21
|
+
self
|
15
22
|
end
|
16
23
|
|
17
24
|
# Public API
|
18
25
|
def times number
|
26
|
+
if mock.__mock_class == Stub
|
27
|
+
raise StubHasNoTimes.new(object, defi, number)
|
28
|
+
end
|
29
|
+
|
19
30
|
if number >= 1
|
20
31
|
(number - 1).times{ mock.__mock_defis_push(defi) }
|
21
32
|
elsif number == 0
|
data/lib/muack/session.rb
CHANGED
@@ -1,18 +1,17 @@
|
|
1
1
|
|
2
2
|
require 'muack/mock'
|
3
3
|
require 'muack/stub'
|
4
|
-
require 'muack/
|
4
|
+
require 'muack/spy'
|
5
5
|
require 'muack/any_instance_of'
|
6
6
|
|
7
7
|
module Muack
|
8
8
|
class Session < Hash
|
9
|
-
def mock
|
10
|
-
def stub
|
11
|
-
def
|
12
|
-
def stub_proxy obj; self["sp #{obj.__id__}"] ||= StubProxy.new(obj); end
|
9
|
+
def mock obj; self["mk #{obj.__id__}"] ||= Mock.new(obj) ; end
|
10
|
+
def stub obj; self["sb #{obj.__id__}"] ||= Stub.new(obj) ; end
|
11
|
+
def spy obj; self["sy #{obj.__id__}"] ||= Spy .new(stub(obj)); end
|
13
12
|
|
14
|
-
def any_instance_of
|
15
|
-
(@
|
13
|
+
def any_instance_of kls
|
14
|
+
(@others ||= {})["ai #{kls.__id__}"] ||= AnyInstanceOf.new(kls)
|
16
15
|
end
|
17
16
|
|
18
17
|
def verify
|
@@ -20,6 +19,7 @@ module Muack
|
|
20
19
|
end
|
21
20
|
|
22
21
|
def reset
|
22
|
+
instance_variable_defined?(:@others) && @others.clear
|
23
23
|
each_value(&:__mock_reset)
|
24
24
|
clear
|
25
25
|
end
|
data/lib/muack/spy.rb
ADDED
@@ -0,0 +1,23 @@
|
|
1
|
+
|
2
|
+
require 'muack/mock'
|
3
|
+
|
4
|
+
module Muack
|
5
|
+
class Spy < Mock
|
6
|
+
def initialize stub
|
7
|
+
super(stub.object)
|
8
|
+
@secret = stub.__mock_disps.values.flatten # steal disps
|
9
|
+
end
|
10
|
+
|
11
|
+
# used for Muack::Session#verify
|
12
|
+
def __mock_verify
|
13
|
+
@secret.each{ |defi| __mock_dispatch(defi.msg, defi.args) }
|
14
|
+
super # simulate dispatching before passing to mock to verify
|
15
|
+
end
|
16
|
+
|
17
|
+
# used for Muack::Session#reset, but spies never leave any track
|
18
|
+
def __mock_reset; end
|
19
|
+
|
20
|
+
private
|
21
|
+
def __mock_inject_method defi; end # spies don't leave any track
|
22
|
+
end
|
23
|
+
end
|
data/lib/muack/stub.rb
CHANGED
@@ -7,12 +7,11 @@ module Muack
|
|
7
7
|
def __mock_verify; true; end
|
8
8
|
|
9
9
|
# used for mocked object to dispatch mocked method
|
10
|
-
def __mock_dispatch msg, actual_args
|
11
|
-
defi = __mock_defis[msg].find{ |d|
|
12
|
-
|
13
|
-
|
14
|
-
|
15
|
-
__mock_block_call(defi, actual_args, actual_block)
|
10
|
+
def __mock_dispatch msg, actual_args
|
11
|
+
if defi = __mock_defis[msg].find{ |d|
|
12
|
+
__mock_check_args(d.args, actual_args) }
|
13
|
+
__mock_disps_push(defi) # our spies are interested in this
|
14
|
+
defi
|
16
15
|
else
|
17
16
|
Mock.__send__(:raise, # Wrong argument
|
18
17
|
Unexpected.new(object, __mock_defis[msg], msg, actual_args))
|
data/lib/muack/version.rb
CHANGED
data/muack.gemspec
CHANGED
@@ -2,11 +2,11 @@
|
|
2
2
|
|
3
3
|
Gem::Specification.new do |s|
|
4
4
|
s.name = "muack"
|
5
|
-
s.version = "0.
|
5
|
+
s.version = "0.7.0"
|
6
6
|
|
7
7
|
s.required_rubygems_version = Gem::Requirement.new(">= 0") if s.respond_to? :required_rubygems_version=
|
8
8
|
s.authors = ["Lin Jen-Shin (godfat)"]
|
9
|
-
s.date = "2013-06-
|
9
|
+
s.date = "2013-06-27"
|
10
10
|
s.description = "Muack -- Yet another mocking library.\n\nBasically it's an [RR][] clone, but much faster under heavy use.\nIt's 32x times faster (750s vs 23s) for running [Rib][] tests.\n\n[RR]: https://github.com/rr/rr\n[Rib]: https://github.com/godfat/rib"
|
11
11
|
s.email = ["godfat (XD) godfat.org"]
|
12
12
|
s.files = [
|
@@ -21,12 +21,13 @@ Gem::Specification.new do |s|
|
|
21
21
|
"lib/muack.rb",
|
22
22
|
"lib/muack/any_instance_of.rb",
|
23
23
|
"lib/muack/definition.rb",
|
24
|
+
"lib/muack/error.rb",
|
24
25
|
"lib/muack/failure.rb",
|
25
26
|
"lib/muack/mock.rb",
|
26
27
|
"lib/muack/modifier.rb",
|
27
|
-
"lib/muack/proxy.rb",
|
28
28
|
"lib/muack/satisfy.rb",
|
29
29
|
"lib/muack/session.rb",
|
30
|
+
"lib/muack/spy.rb",
|
30
31
|
"lib/muack/stub.rb",
|
31
32
|
"lib/muack/test.rb",
|
32
33
|
"lib/muack/version.rb",
|
@@ -2,8 +2,9 @@
|
|
2
2
|
require 'muack/test'
|
3
3
|
|
4
4
|
describe Muack::AnyInstanceOf do
|
5
|
+
klass = Class.new{ def f; 0; end }
|
6
|
+
|
5
7
|
should 'mock any_instance_of' do
|
6
|
-
klass = Class.new
|
7
8
|
any_instance_of(klass){ |instance| mock(instance).say{ true } }
|
8
9
|
obj = klass.new
|
9
10
|
obj.say .should.eq true
|
@@ -13,17 +14,15 @@ describe Muack::AnyInstanceOf do
|
|
13
14
|
end
|
14
15
|
|
15
16
|
should 'proxy any_instance_of' do
|
16
|
-
klass
|
17
|
-
any_instance_of(klass){ |instance| mock_proxy(instance).f }
|
17
|
+
any_instance_of(klass){ |instance| mock(instance).f.proxy }
|
18
18
|
obj = klass.new
|
19
|
-
obj.f .should.eq
|
19
|
+
obj.f .should.eq 0
|
20
20
|
Muack.verify.should.eq true
|
21
|
-
obj.f .should.eq
|
21
|
+
obj.f .should.eq 0
|
22
22
|
end
|
23
23
|
|
24
24
|
should 'proxy any_instance_of with a block' do
|
25
|
-
klass
|
26
|
-
any_instance_of(klass){ |instance| mock_proxy(instance).f{ |i| i+1 } }
|
25
|
+
any_instance_of(klass){ |instance| mock(instance).f{ |i| i+1 }.proxy }
|
27
26
|
obj = klass.new
|
28
27
|
obj.f .should.eq 1
|
29
28
|
Muack.verify.should.eq true
|
@@ -31,9 +30,8 @@ describe Muack::AnyInstanceOf do
|
|
31
30
|
end
|
32
31
|
|
33
32
|
should 'proxy with multiple any_instance_of call' do
|
34
|
-
klass
|
35
|
-
any_instance_of(klass){ |instance|
|
36
|
-
any_instance_of(klass){ |instance| mock_proxy(instance).f{ |i| i+2 } }
|
33
|
+
any_instance_of(klass){ |instance| mock(instance).f{ |i| i+1 }.proxy }
|
34
|
+
any_instance_of(klass){ |instance| mock(instance).f{ |i| i+2 }.proxy }
|
37
35
|
obj = klass.new
|
38
36
|
obj.f.should.eq 1
|
39
37
|
obj.f.should.eq 2
|
@@ -42,13 +40,54 @@ describe Muack::AnyInstanceOf do
|
|
42
40
|
end
|
43
41
|
|
44
42
|
should 'mock with multiple any_instance_of call' do
|
45
|
-
klass = Class.new{ def f; 0; end }
|
46
43
|
any_instance_of(klass){ |inst| mock(inst).f(is_a(Fixnum)){ |i| i+1 } }
|
47
44
|
any_instance_of(klass){ |inst| mock(inst).f(is_a(Fixnum)){ |i| i+2 } }
|
48
45
|
obj = klass.new
|
49
|
-
obj.f(
|
50
|
-
obj.f(
|
46
|
+
obj.f(2).should.eq 3
|
47
|
+
obj.f(2).should.eq 4
|
48
|
+
Muack.verify.should.eq true
|
49
|
+
obj.f.should.eq 0
|
50
|
+
end
|
51
|
+
|
52
|
+
should 'stub proxy with any_instance_of and spy' do
|
53
|
+
any_instance_of(klass){ |inst| stub(inst).f{ |i| i+3 }.proxy }
|
54
|
+
obj = klass.new
|
55
|
+
obj.f.should.eq 3
|
56
|
+
obj.f.should.eq 3
|
57
|
+
spy(any_instance_of(klass)).f.times(2)
|
51
58
|
Muack.verify.should.eq true
|
52
59
|
obj.f.should.eq 0
|
53
60
|
end
|
61
|
+
|
62
|
+
should 'stub with any_instance_of and spy under satisfied' do
|
63
|
+
any_instance_of(klass){ |inst| stub(inst).f{ 5 } }
|
64
|
+
obj = klass.new
|
65
|
+
obj.f.should.eq 5
|
66
|
+
spy(any_instance_of(klass)).f.times(2)
|
67
|
+
begin
|
68
|
+
Muack.verify
|
69
|
+
rescue Muack::Expected => e
|
70
|
+
expected = /Muack::API\.any_instance_of\(.+?\)\.f\(\)/
|
71
|
+
e.expected .should =~ expected
|
72
|
+
e.expected_times.should.eq 2
|
73
|
+
e.actual_times .should.eq 1
|
74
|
+
end
|
75
|
+
obj.f.should.eq 0
|
76
|
+
end
|
77
|
+
|
78
|
+
should 'stub with any_instance_of and spy over satisfied' do
|
79
|
+
any_instance_of(klass){ |inst| stub(inst).f{ 2 } }
|
80
|
+
obj = klass.new
|
81
|
+
2.times{ obj.f.should.eq 2 }
|
82
|
+
spy(any_instance_of(klass)).f
|
83
|
+
begin
|
84
|
+
Muack.verify
|
85
|
+
rescue Muack::Expected => e
|
86
|
+
expected = /Muack::API\.any_instance_of\(.+?\)\.f\(\)/
|
87
|
+
e.expected .should =~ expected
|
88
|
+
e.expected_times.should.eq 1
|
89
|
+
e.actual_times .should.eq 2
|
90
|
+
end
|
91
|
+
obj.f.should.eq 0
|
92
|
+
end
|
54
93
|
end
|
data/test/test_mock.rb
CHANGED
@@ -8,6 +8,10 @@ describe Muack::Mock do
|
|
8
8
|
Muack::EnsureReset.call
|
9
9
|
end
|
10
10
|
|
11
|
+
should 'inspect' do
|
12
|
+
mock(Obj).inspect.should.eq "Muack::API.mock(obj)"
|
13
|
+
end
|
14
|
+
|
11
15
|
should 'mock with regular method' do
|
12
16
|
mock(Obj).say(true){ 'boo' }
|
13
17
|
Obj.say(true).should.eq 'boo'
|
@@ -58,10 +62,6 @@ describe Muack::Mock do
|
|
58
62
|
mock.say{1}.object.say.should.eq 1
|
59
63
|
end
|
60
64
|
|
61
|
-
should 'inspect' do
|
62
|
-
mock(Obj).inspect.should.eq "#<Muack::Mock object=obj>"
|
63
|
-
end
|
64
|
-
|
65
65
|
should 'mock and call, mock and call' do
|
66
66
|
mock(Obj).say{0}
|
67
67
|
Obj.say.should.eq 0
|
@@ -94,7 +94,7 @@ describe Muack::Mock do
|
|
94
94
|
Muack::EnsureReset.call
|
95
95
|
end
|
96
96
|
|
97
|
-
should 'raise
|
97
|
+
should 'raise Unexpected error if passing unexpected argument' do
|
98
98
|
mock(Obj).say(true){ 'boo' }
|
99
99
|
begin
|
100
100
|
Obj.say(false)
|
@@ -144,7 +144,7 @@ describe Muack::Mock do
|
|
144
144
|
end
|
145
145
|
end
|
146
146
|
|
147
|
-
should 'raise
|
147
|
+
should 'raise Unexpected when calling with diff sig' do
|
148
148
|
mock(Obj).say(true){1}
|
149
149
|
Obj.say(true).should.eq 1
|
150
150
|
begin
|
@@ -157,7 +157,7 @@ describe Muack::Mock do
|
|
157
157
|
end
|
158
158
|
end
|
159
159
|
|
160
|
-
should 'raise
|
160
|
+
should 'raise Expected error if mock methods not called' do
|
161
161
|
mock(Obj).say(true){ 'boo' }
|
162
162
|
begin
|
163
163
|
Muack.verify
|
data/test/test_proxy.rb
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
|
2
2
|
require 'muack/test'
|
3
3
|
|
4
|
-
describe Muack::
|
4
|
+
describe Muack::Mock do
|
5
5
|
describe 'Muack.verify==true' do
|
6
6
|
after do
|
7
7
|
Muack.verify.should.eq true
|
@@ -9,48 +9,60 @@ describe Muack::Proxy do
|
|
9
9
|
end
|
10
10
|
|
11
11
|
should 'proxy with regular method' do
|
12
|
-
|
12
|
+
mock(Str).reverse.proxy
|
13
13
|
Str.reverse.should.eq 'ooM'
|
14
14
|
end
|
15
15
|
|
16
16
|
should 'proxy multiple times' do
|
17
|
-
2.times{
|
17
|
+
2.times{ mock(Str).reverse.proxy }
|
18
18
|
2.times{ Str.reverse.should.eq 'ooM' }
|
19
19
|
end
|
20
20
|
|
21
21
|
should 'proxy multiple times with super method' do
|
22
|
-
2.times{
|
22
|
+
2.times{ mock(Str).class.proxy }
|
23
23
|
2.times{ Str.class.should.eq String }
|
24
24
|
end
|
25
25
|
|
26
|
+
should 'return modifier itself for any modifier methods' do
|
27
|
+
mock(Str).to_s.proxy.returns{ |s| s.reverse }.times(2).
|
28
|
+
with_any_args.with_any_args
|
29
|
+
2.times{ Str.to_s.should.eq 'ooM' }
|
30
|
+
end
|
31
|
+
|
26
32
|
should 'proxy and call the block' do
|
27
|
-
|
33
|
+
mock(Obj).method_missing(:inspect){ |str| str.reverse }.proxy
|
28
34
|
Obj.inspect.should.eq 'jbo'
|
29
35
|
end
|
30
36
|
|
31
37
|
should 'proxy and call the block with super' do
|
32
|
-
|
38
|
+
mock(Str).class{ |k| k.name.reverse }.proxy
|
33
39
|
Str.class.should.eq 'gnirtS'
|
34
40
|
end
|
35
41
|
|
36
|
-
should '
|
37
|
-
|
42
|
+
should 'mock proxy and call, mock proxy and call' do
|
43
|
+
mock(Obj).class{ |k| k.name.reverse }.proxy
|
38
44
|
Obj.class.should.eq 'tcejbO'
|
39
|
-
|
45
|
+
mock(Obj).class{ |k| k.name.upcase }.proxy
|
40
46
|
Obj.class.should.eq 'OBJECT'
|
41
47
|
end
|
42
48
|
|
43
|
-
should '
|
44
|
-
|
49
|
+
should 'stub proxy and call, stub proxy and call' do
|
50
|
+
stub(Obj).kind_of?(Object){ |b| !b }.proxy
|
45
51
|
Obj.kind_of?(Object).should.eq false
|
46
|
-
|
52
|
+
stub(Obj).kind_of?(String){ |b| b.to_s }.proxy
|
47
53
|
Obj.kind_of?(String).should.eq 'false'
|
48
54
|
end
|
49
55
|
|
50
|
-
should '
|
51
|
-
|
56
|
+
should 'stub proxy with any times' do
|
57
|
+
stub(Obj).class{ |k| k.name.downcase }.proxy
|
52
58
|
3.times{ Obj.class.should.eq 'object' }
|
53
59
|
end
|
60
|
+
|
61
|
+
should 'stub proxy and spy' do
|
62
|
+
stub(Obj).class{ |k| k.name.downcase }.proxy
|
63
|
+
Obj.class.should.eq 'object'
|
64
|
+
spy(Obj).class
|
65
|
+
end
|
54
66
|
end
|
55
67
|
|
56
68
|
describe 'Muack.verify==false' do
|
@@ -59,8 +71,8 @@ describe Muack::Proxy do
|
|
59
71
|
Muack::EnsureReset.call
|
60
72
|
end
|
61
73
|
|
62
|
-
should 'raise
|
63
|
-
|
74
|
+
should 'raise Expected error if passing unexpected argument' do
|
75
|
+
mock(Str).reverse.proxy
|
64
76
|
Str.reverse.should.eq 'ooM'
|
65
77
|
begin
|
66
78
|
Str.reverse
|
data/test/test_satisfy.rb
CHANGED
@@ -17,7 +17,7 @@ describe Muack::Satisfy do
|
|
17
17
|
Muack::EnsureReset.call
|
18
18
|
end
|
19
19
|
|
20
|
-
should 'raise
|
20
|
+
should 'raise Unexpected error if passing unexpected argument' do
|
21
21
|
mock(Obj).say(is_a(Array)){ 'boo' }
|
22
22
|
begin
|
23
23
|
Obj.say(false)
|
@@ -53,7 +53,7 @@ describe Muack::Satisfy do
|
|
53
53
|
Muack::EnsureReset.call
|
54
54
|
end
|
55
55
|
|
56
|
-
should 'raise
|
56
|
+
should 'raise Unexpected error if passing unexpected argument' do
|
57
57
|
mock(Obj).say(anything){ 'boo' }
|
58
58
|
begin
|
59
59
|
Obj.say(6, 7)
|
@@ -84,7 +84,7 @@ describe Muack::Satisfy do
|
|
84
84
|
Muack::EnsureReset.call
|
85
85
|
end
|
86
86
|
|
87
|
-
should 'raise
|
87
|
+
should 'raise Unexpected error if passing unexpected argument' do
|
88
88
|
mock(Obj).say(match(/\w/)){ 'boo' }
|
89
89
|
begin
|
90
90
|
Obj.say('!')
|
@@ -115,7 +115,7 @@ describe Muack::Satisfy do
|
|
115
115
|
Muack::EnsureReset.call
|
116
116
|
end
|
117
117
|
|
118
|
-
should 'raise
|
118
|
+
should 'raise Unexpected error if passing unexpected argument' do
|
119
119
|
mock(Obj).say(hash_including(:b => 2)){ 'boo' }
|
120
120
|
begin
|
121
121
|
Obj.say(:a => 1)
|
@@ -151,7 +151,7 @@ describe Muack::Satisfy do
|
|
151
151
|
Muack::EnsureReset.call
|
152
152
|
end
|
153
153
|
|
154
|
-
should 'raise
|
154
|
+
should 'raise Unexpected error if passing unexpected argument' do
|
155
155
|
mock(Obj).say(within(0..5)){ 'boo' }
|
156
156
|
begin
|
157
157
|
Obj.say(6)
|
@@ -182,7 +182,7 @@ describe Muack::Satisfy do
|
|
182
182
|
Muack::EnsureReset.call
|
183
183
|
end
|
184
184
|
|
185
|
-
should 'raise
|
185
|
+
should 'raise Unexpected error if passing unexpected argument' do
|
186
186
|
mock(Obj).say(satisfy{ |arg| arg % 2 == 0 }){ 'boo' }
|
187
187
|
begin
|
188
188
|
Obj.say(1)
|
data/test/test_stub.rb
CHANGED
@@ -2,12 +2,24 @@
|
|
2
2
|
require 'muack/test'
|
3
3
|
|
4
4
|
describe Muack::Stub do
|
5
|
+
should 'raise StubHasNoTimes with stub(obj).f.times(0)' do
|
6
|
+
lambda{ stub(Obj).f.times(0) }.should.raise(Muack::StubHasNoTimes)
|
7
|
+
end
|
8
|
+
|
5
9
|
describe 'Muack.verify==true' do
|
6
10
|
after do
|
7
11
|
Muack.verify.should.eq true
|
8
12
|
Muack::EnsureReset.call
|
9
13
|
end
|
10
14
|
|
15
|
+
should 'inspect' do
|
16
|
+
stub(Obj).inspect.should.eq "Muack::API.stub(obj)"
|
17
|
+
end
|
18
|
+
|
19
|
+
should 'inspect' do
|
20
|
+
spy( Obj).inspect.should.eq "Muack::API.spy(obj)"
|
21
|
+
end
|
22
|
+
|
11
23
|
should 'stub with regular method' do
|
12
24
|
stub(Obj).say{ 'goo' }
|
13
25
|
3.times{ Obj.say.should.eq 'goo' }
|
@@ -26,6 +38,24 @@ describe Muack::Stub do
|
|
26
38
|
Obj.saya.should.eq 1
|
27
39
|
Obj.say .should.eq 0
|
28
40
|
end
|
41
|
+
|
42
|
+
should 'work with spy' do
|
43
|
+
stub(Obj).say{0}
|
44
|
+
Obj.say.should.eq 0
|
45
|
+
spy(Obj).say
|
46
|
+
end
|
47
|
+
|
48
|
+
should 'work with spy twice' do
|
49
|
+
stub(Obj).say
|
50
|
+
2.times{ Obj.say.should.eq nil }
|
51
|
+
spy(Obj).say.times(2)
|
52
|
+
end
|
53
|
+
|
54
|
+
should 'work with spy spy' do
|
55
|
+
stub(Obj).say
|
56
|
+
2.times{ Obj.say.should.eq nil }
|
57
|
+
2.times{ spy(Obj).say }
|
58
|
+
end
|
29
59
|
end
|
30
60
|
|
31
61
|
describe 'Muack.verify==false' do
|
@@ -34,7 +64,7 @@ describe Muack::Stub do
|
|
34
64
|
Muack::EnsureReset.call
|
35
65
|
end
|
36
66
|
|
37
|
-
should 'raise
|
67
|
+
should 'raise Unexpected error if passing unexpected argument' do
|
38
68
|
stub(Obj).say(true){ 'boo' }
|
39
69
|
begin
|
40
70
|
Obj.say(false)
|
@@ -58,5 +88,66 @@ describe Muack::Stub do
|
|
58
88
|
e.message .should.eq "\nExpected: #{e.expected}\n but was: #{e.was}"
|
59
89
|
end
|
60
90
|
end
|
91
|
+
|
92
|
+
should 'raise Expected if the spy is not satisfied' do
|
93
|
+
stub(Obj).say
|
94
|
+
spy( Obj).say
|
95
|
+
begin
|
96
|
+
Muack.verify
|
97
|
+
'never'.should.eq 'reach'
|
98
|
+
rescue Muack::Expected => e
|
99
|
+
e.expected .should.eq 'obj.say()'
|
100
|
+
e.expected_times.should.eq 1
|
101
|
+
e.actual_times .should.eq 0
|
102
|
+
e.message .should.eq "\nExpected: obj.say()\n " \
|
103
|
+
"called 1 times\n but was 0 times."
|
104
|
+
end
|
105
|
+
end
|
106
|
+
|
107
|
+
should 'raise Expected if the spy is not satisfied enough' do
|
108
|
+
stub(Obj).say
|
109
|
+
Obj.say
|
110
|
+
spy( Obj).say(0)
|
111
|
+
begin
|
112
|
+
Muack.verify
|
113
|
+
'never'.should.eq 'reach'
|
114
|
+
rescue Muack::Unexpected => e
|
115
|
+
e.expected.should.eq "obj.say(0)"
|
116
|
+
e.was .should.eq 'obj.say()'
|
117
|
+
e.message .should.eq "\nExpected: #{e.expected}\n but was: #{e.was}"
|
118
|
+
end
|
119
|
+
end
|
120
|
+
|
121
|
+
should 'show correct times for under satisfaction' do
|
122
|
+
stub(Obj).say
|
123
|
+
2.times{ Obj.say }
|
124
|
+
spy( Obj).say.times(3)
|
125
|
+
begin
|
126
|
+
Muack.verify
|
127
|
+
'never'.should.eq 'reach'
|
128
|
+
rescue Muack::Expected => e
|
129
|
+
e.expected .should.eq 'obj.say()'
|
130
|
+
e.expected_times.should.eq 3
|
131
|
+
e.actual_times .should.eq 2
|
132
|
+
e.message .should.eq "\nExpected: obj.say()\n " \
|
133
|
+
"called 3 times\n but was 2 times."
|
134
|
+
end
|
135
|
+
end
|
136
|
+
|
137
|
+
should 'show correct times for over satisfaction' do
|
138
|
+
stub(Obj).say
|
139
|
+
2.times{ Obj.say }
|
140
|
+
spy( Obj).say
|
141
|
+
begin
|
142
|
+
Muack.verify
|
143
|
+
'never'.should.eq 'reach'
|
144
|
+
rescue Muack::Expected => e
|
145
|
+
e.expected .should.eq 'obj.say()'
|
146
|
+
e.expected_times.should.eq 1
|
147
|
+
e.actual_times .should.eq 2
|
148
|
+
e.message .should.eq "\nExpected: obj.say()\n " \
|
149
|
+
"called 1 times\n but was 2 times."
|
150
|
+
end
|
151
|
+
end
|
61
152
|
end
|
62
153
|
end
|
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: muack
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.
|
4
|
+
version: 0.7.0
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Lin Jen-Shin (godfat)
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date: 2013-06-
|
11
|
+
date: 2013-06-27 00:00:00.000000000 Z
|
12
12
|
dependencies: []
|
13
13
|
description: |-
|
14
14
|
Muack -- Yet another mocking library.
|
@@ -35,12 +35,13 @@ files:
|
|
35
35
|
- lib/muack.rb
|
36
36
|
- lib/muack/any_instance_of.rb
|
37
37
|
- lib/muack/definition.rb
|
38
|
+
- lib/muack/error.rb
|
38
39
|
- lib/muack/failure.rb
|
39
40
|
- lib/muack/mock.rb
|
40
41
|
- lib/muack/modifier.rb
|
41
|
-
- lib/muack/proxy.rb
|
42
42
|
- lib/muack/satisfy.rb
|
43
43
|
- lib/muack/session.rb
|
44
|
+
- lib/muack/spy.rb
|
44
45
|
- lib/muack/stub.rb
|
45
46
|
- lib/muack/test.rb
|
46
47
|
- lib/muack/version.rb
|
data/lib/muack/proxy.rb
DELETED
@@ -1,39 +0,0 @@
|
|
1
|
-
|
2
|
-
require 'muack/mock'
|
3
|
-
require 'muack/stub'
|
4
|
-
|
5
|
-
module Muack
|
6
|
-
module Proxy
|
7
|
-
def __mock_block_call defi, actual_args, actual_block
|
8
|
-
# handle block call in injected method, since we need to call origin
|
9
|
-
defi # but we still want to know which defi gets dispatched!
|
10
|
-
end
|
11
|
-
|
12
|
-
def __mock_inject_mock_method target, defi
|
13
|
-
mock = self # remember the context
|
14
|
-
target.__send__(:define_method, defi.msg){|*actual_args, &actual_block|
|
15
|
-
d = mock.__mock_dispatch(defi.msg, actual_args, actual_block)
|
16
|
-
|
17
|
-
ret = if d.original_method
|
18
|
-
__send__(d.original_method, *actual_args, &actual_block)
|
19
|
-
else
|
20
|
-
super(*actual_args, &actual_block)
|
21
|
-
end
|
22
|
-
|
23
|
-
if d.block
|
24
|
-
d.block.call(ret)
|
25
|
-
else
|
26
|
-
ret
|
27
|
-
end
|
28
|
-
}
|
29
|
-
end
|
30
|
-
end
|
31
|
-
|
32
|
-
class MockProxy < Mock
|
33
|
-
include Proxy
|
34
|
-
end
|
35
|
-
|
36
|
-
class StubProxy < Stub
|
37
|
-
include Proxy
|
38
|
-
end
|
39
|
-
end
|