muack 0.5.2 → 0.7.0

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: b0cc50c953e21826fe85b70e03238b3c22486195
4
- data.tar.gz: 621fad09a9fb9fe21edd940d33e60d0415c12c5f
3
+ metadata.gz: f7727020cca0c41dfd846c0b7628aec71c2cbb59
4
+ data.tar.gz: 9fb966eb9e9e1dc65d8f0e938d2d0a619813f204
5
5
  SHA512:
6
- metadata.gz: f9453f77aeadc5efcecfd33e6785a7ec4d8700c01e72367910f6b68be0480dc2e3699f87c5658c2ce41601ad78b3f3ca2800ba78aa4ccde9d8ee6ea8234e14a8
7
- data.tar.gz: 0d69e0c4d3c544ef7e69c2b60691f369086cac70f2284167dadfc03613087fa2bd78476675ddf9f8504a811eca1abbeb625312082b33f043bb241ccf8928ba41
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
- #### [mock_proxy](https://github.com/rr/rr/blob/e4b4907fd0488738affb4dab8ce88cbe9fa6580e/doc/03_api_overview.md#mockproxy)
104
+ #### [proxy](https://github.com/rr/rr/blob/e4b4907fd0488738affb4dab8ce88cbe9fa6580e/doc/03_api_overview.md#mockproxy)
105
105
 
106
- Since I don't see how we gain from calling it mock.proxy, in Muack we
107
- just call it `mock_proxy`.
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
- mock_proxy(view).render(:partial => "right_navigation")
112
- mock_proxy(view).render(:partial => "user_info") do |html|
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
- #### [stub_proxy](https://github.com/rr/rr/blob/e4b4907fd0488738affb4dab8ce88cbe9fa6580e/doc/03_api_overview.md#stubproxy)
130
+ The same goes to `stub`.
119
131
 
120
- The same goes to `stub_proxy`.
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
- stub_proxy(view).render(:partial => "user_info") do |html|
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
- @session ||= Muack::Session.new
13
+ Thread.current[:muack_session] ||= Muack::Session.new
14
14
  end
15
15
 
16
16
  def self.reset
17
- @session && @session.reset
18
- @session = nil
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 mock_proxy obj=Object.new
34
- ret = Muack.session.mock_proxy(obj)
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
 
@@ -1,4 +1,8 @@
1
1
 
2
2
  module Muack
3
- AnyInstanceOf = Class.new(Struct.new(:singleton_class))
3
+ class AnyInstanceOf < Struct.new(:singleton_class)
4
+ def inspect
5
+ "Muack::API.any_instance_of(#{singleton_class})"
6
+ end
7
+ end
4
8
  end
@@ -1,5 +1,6 @@
1
1
 
2
2
  module Muack
3
- Definition = Class.new(Struct.new(:msg, :args, :block, :original_method))
3
+ Definition = Class.new(Struct.new(:msg, :args, :block,
4
+ :original_method, :proxy))
4
5
  WithAnyArgs = Object.new
5
6
  end
@@ -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
- "#<#{class << self; self; end.superclass} object=#{object.inspect}>"
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, actual_block
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
- __mock_block_call(defi, actual_args, actual_block)
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
- raise "Cannot find a suitable method name, tried #{level+1} times." if
112
- level >= 9
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, actual_block)
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
@@ -1,5 +1,5 @@
1
1
 
2
- require 'muack/definition'
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/proxy'
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 obj; self["mk #{obj.__id__}"] ||= Mock .new(obj); end
10
- def stub obj; self["sb #{obj.__id__}"] ||= Stub .new(obj); end
11
- def mock_proxy obj; self["mp #{obj.__id__}"] ||= MockProxy.new(obj); end
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 klass
15
- (@any_instance_of ||= {})[klass.__id__] ||= AnyInstanceOf.new(klass)
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, actual_block
11
- defi = __mock_defis[msg].find{ |d|
12
- __mock_check_args(d.args, actual_args)
13
- }
14
- if defi
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
@@ -1,4 +1,4 @@
1
1
 
2
2
  module Muack
3
- VERSION = '0.5.2'
3
+ VERSION = '0.7.0'
4
4
  end
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.2"
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-26"
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 = Class.new{ def f; true; end }
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 true
19
+ obj.f .should.eq 0
20
20
  Muack.verify.should.eq true
21
- obj.f .should.eq true
21
+ obj.f .should.eq 0
22
22
  end
23
23
 
24
24
  should 'proxy any_instance_of with a block' do
25
- klass = Class.new{ def f; 0; end }
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 = Class.new{ def f; 0; end }
35
- any_instance_of(klass){ |instance| mock_proxy(instance).f{ |i| i+1 } }
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(0).should.eq 1
50
- obj.f(0).should.eq 2
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 Muack::Unexpected error if passing unexpected argument' do
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 Muack::Unexpected when calling with diff sig' do
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 Muack::Expected error if mock methods not called' do
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::Proxy do
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
- mock_proxy(Str).reverse
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{ mock_proxy(Str).reverse }
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{ mock_proxy(Str).class }
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
- mock_proxy(Obj).method_missing(:inspect){ |str| str.reverse }
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
- mock_proxy(Str).class{ |k| k.name.reverse }
38
+ mock(Str).class{ |k| k.name.reverse }.proxy
33
39
  Str.class.should.eq 'gnirtS'
34
40
  end
35
41
 
36
- should 'mock_proxy and call, mock_proxy and call' do
37
- mock_proxy(Obj).class{ |k| k.name.reverse }
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
- mock_proxy(Obj).class{ |k| k.name.upcase }
45
+ mock(Obj).class{ |k| k.name.upcase }.proxy
40
46
  Obj.class.should.eq 'OBJECT'
41
47
  end
42
48
 
43
- should 'stub_proxy and call, stub_proxy and call' do
44
- stub_proxy(Obj).kind_of?(Object){ |b| !b }
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
- stub_proxy(Obj).kind_of?(String){ |b| b.to_s }
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 'stub_proxy with any times' do
51
- stub_proxy(Obj).class{ |k| k.name.downcase }
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 Muack::Expected error if passing unexpected argument' do
63
- mock_proxy(Str).reverse
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 Muack::Unexpected error if passing unexpected argument' do
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 Muack::Unexpected error if passing unexpected argument' do
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 Muack::Unexpected error if passing unexpected argument' do
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 Muack::Unexpected error if passing unexpected argument' do
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 Muack::Unexpected error if passing unexpected argument' do
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 Muack::Unexpected error if passing unexpected argument' do
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 Muack::Unexpected error if passing unexpected argument' do
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.5.2
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-26 00:00:00.000000000 Z
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