flexmock 0.3.0 → 0.3.1

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/CHANGELOG CHANGED
@@ -1,5 +1,13 @@
1
1
  = Changes for FlexMock
2
2
 
3
+ == Version 0.3.1
4
+
5
+ * Fixed some warnings regarding uniniitalized variables.
6
+ * Added (very) simple class interception.
7
+ * Added the mock_factory method to go along with class interception.
8
+ * When using Test::Unit integration, avoid mock verification if the
9
+ test has already failed for some reason.
10
+
3
11
  == Version 0.3.0
4
12
 
5
13
  * Added Test::Unit integration.
data/README CHANGED
@@ -3,7 +3,7 @@
3
3
  FlexMock is a simple mock object for unit testing. The interface is
4
4
  simple, but still provides a good bit of flexibility.
5
5
 
6
- Version :: 0.3.0
6
+ Version :: 0.3.1
7
7
 
8
8
  = Links
9
9
 
@@ -215,6 +215,25 @@ The following rules are used for argument matching:
215
215
 
216
216
  will match any even integer.
217
217
 
218
+ === Class Interception
219
+
220
+ FlexMock now support simple class interception. For the duration of a
221
+ test, a mock class take the place of a named class inside the class to
222
+ be tested.
223
+
224
+ Example:
225
+
226
+ Suppose we are testing class Foo, and Foo uses Bar internally. We
227
+ would like for Bar.new to return a mock object during the test.
228
+
229
+ def test_foo_with_an_intercepted_bar
230
+ my_mock = flexmock("my_mock").should_receive(....).mock
231
+ intercept(Bar).in(Foo).with(my_mock.mock_factory)
232
+
233
+ bar = Bar.new
234
+ bar.do_something
235
+ end
236
+
218
237
  == Examples
219
238
 
220
239
  === Expect multiple queries and a single update
data/Rakefile CHANGED
@@ -8,7 +8,7 @@ require 'rake/testtask'
8
8
 
9
9
  CLOBBER.include("html", 'pkg')
10
10
 
11
- PKG_VERSION = '0.3.0'
11
+ PKG_VERSION = '0.3.1'
12
12
 
13
13
  PKG_FILES = FileList[
14
14
  '[A-Z]*',
@@ -34,6 +34,7 @@ task :default => [:test]
34
34
  Rake::TestTask.new do |t|
35
35
  t.pattern = 'test/test*.rb'
36
36
  t.verbose = true
37
+ t.warning = true
37
38
  end
38
39
 
39
40
  # RDoc Target --------------------------------------------------------
data/lib/flexmock.rb CHANGED
@@ -60,6 +60,7 @@ class FlexMock
60
60
  @allocated_order = 0
61
61
  @mock_current_order = 0
62
62
  @mock_groups = {}
63
+ @ignore_missing = false
63
64
  end
64
65
 
65
66
  # Handle all messages denoted by +sym+ by calling the given block
@@ -150,6 +151,12 @@ class FlexMock
150
151
  yield Recorder.new(self)
151
152
  end
152
153
 
154
+ # Return a factory object that returns this mock. This is useful in
155
+ # Class Interception.
156
+ def mock_factory
157
+ Factory.new(self)
158
+ end
159
+
153
160
  class << self
154
161
  include Test::Unit::Assertions
155
162
 
@@ -216,6 +223,27 @@ class FlexMock
216
223
  ex.backtrace
217
224
  end
218
225
 
226
+ ####################################################################
227
+ # A Factory object is returned from a mock_factory method call. The
228
+ # factory merely returns the manufactured object it is initialized
229
+ # with. The factory is handy to use with class interception,
230
+ # allowing the intercepted class to return the mock object.
231
+ #
232
+ # If the user needs more control over the mock factory, they are
233
+ # free to create their own.
234
+ #
235
+ # Typical Usage:
236
+ # intercept(Bar).in(Foo).with(a_mock.mack_factory)
237
+ #
238
+ class Factory
239
+ def initialize(manufactured_object)
240
+ @obj = manufactured_object
241
+ end
242
+ def new(*args, &block)
243
+ @obj
244
+ end
245
+ end
246
+
219
247
  ####################################################################
220
248
  # The expectation director is responsible for routing calls to the
221
249
  # correct expectations for a given argument list.
@@ -691,9 +719,15 @@ class FlexMock
691
719
 
692
720
  # Do the flexmock specific teardown stuff.
693
721
  def flexmock_teardown
694
- return if @flexmock_created_mocks.nil?
695
- @flexmock_created_mocks.each do |m|
696
- m.mock_verify
722
+ @flexmock_created_mocks ||= []
723
+ if passed?
724
+ @flexmock_created_mocks.each do |m|
725
+ m.mock_verify
726
+ end
727
+ end
728
+ @flexmock_interceptors ||= []
729
+ @flexmock_interceptors.each do |i|
730
+ i.restore
697
731
  end
698
732
  end
699
733
 
@@ -706,5 +740,133 @@ class FlexMock
706
740
  @flexmock_created_mocks << result
707
741
  result
708
742
  end
743
+
744
+ # Intercept the named class in the target class for the duration
745
+ # of the test. Class interception is very simple-minded and has a
746
+ # number of restrictions. First, the intercepted class must be
747
+ # reference in the tested class via a simple constant name
748
+ # (e.g. no scoped names using "::") that is not directly defined
749
+ # in the class itself. After the test, a proxy class constant
750
+ # will be left behind that will forward all calls to the original
751
+ # class.
752
+ #
753
+ # Usage:
754
+ # intercept(SomeClass).in(ClassBeingTested).with(MockClass)
755
+ # intercept(SomeClass).with(MockClass).in(ClassBeingTested)
756
+ #
757
+ def intercept(intercepted_class)
758
+ result = Interception.new(intercepted_class)
759
+ @flexmock_interceptors ||= []
760
+ @flexmock_interceptors << result
761
+ result
762
+ end
709
763
  end
764
+
765
+ ####################################################################
766
+ # A Class Interception defines a constant in the target class to be
767
+ # a proxy that points to a replacement class for the duration of a
768
+ # test. When an interception is restored, the proxy will point to
769
+ # the original intercepted class.
770
+ #
771
+ class Interception
772
+ # Create an interception object with the class to intercepted.
773
+ def initialize(intercepted_class)
774
+ @intercepted = nil
775
+ @target = nil
776
+ @replacement = nil
777
+ intercept(intercepted_class)
778
+ update
779
+ end
780
+
781
+ # Intercept this class in the class to be tested.
782
+ def intercept(intercepted_class)
783
+ @intercepted = intercepted_class
784
+ update
785
+ self
786
+ end
787
+
788
+ # Define the class number test that will receive the
789
+ # interceptioned definition.
790
+ def in(target_class)
791
+ @target = target_class
792
+ update
793
+ self
794
+ end
795
+
796
+ # Define the replacement class. This is normally a proxy or a
797
+ # stub.
798
+ def with(replacement_class)
799
+ @replacement = replacement_class
800
+ update
801
+ self
802
+ end
803
+
804
+ # Restore the original class. The proxy remains in place however.
805
+ def restore
806
+ @proxy.proxied_class = @restore_class if @proxy
807
+ end
808
+
809
+ private
810
+
811
+ # Update the interception if the definition is complete.
812
+ def update
813
+ if complete?
814
+ do_interception
815
+ end
816
+ end
817
+
818
+ # Is the interception definition complete. In other words, are
819
+ # all three actors defined?
820
+ def complete?
821
+ @intercepted && @target && @replacement
822
+ end
823
+
824
+ # Implement interception on the classes defined.
825
+ def do_interception
826
+ @target_class = coerce_class(@target)
827
+ @replacement_class = coerce_class(@replacement)
828
+ case @intercepted
829
+ when String, Symbol
830
+ @intercepted_name = @intercepted.to_s
831
+ when Class
832
+ @intercepted_name = @intercepted.name
833
+ end
834
+ @intercepted_class = coerce_class(@intercepted)
835
+ current_class = @target_class.const_get(@intercepted_name)
836
+ if ClassProxy === current_class
837
+ @proxy = current_class
838
+ @restore_class = @proxy.proxied_class
839
+ @proxy.proxied_class = @replacement_class
840
+ else
841
+ @proxy = ClassProxy.new(@replacement_class)
842
+ @restore_class = current_class
843
+ @target_class.const_set(@intercepted_name, @proxy)
844
+ end
845
+ end
846
+
847
+ # Coerce a class object, string to symbol to be the class object.
848
+ def coerce_class(klass)
849
+ case klass
850
+ when String, Symbol
851
+ Object.const_get(klass.to_s)
852
+ when Class
853
+ klass
854
+ end
855
+ end
856
+ end
857
+
858
+ ####################################################################
859
+ # Class Proxy for class interception. Forward all method calls to
860
+ # whatever is the proxied_class.
861
+ #
862
+ class ClassProxy
863
+ attr_accessor :proxied_class
864
+ def initialize(default_class)
865
+ @proxied_class = default_class
866
+ end
867
+ def method_missing(sym, *args, &block)
868
+ @proxied_class.__send__(sym, *args, &block)
869
+ end
870
+ end
871
+
710
872
  end
@@ -0,0 +1,108 @@
1
+ #!/usr/bin/env ruby
2
+
3
+ require 'test/unit'
4
+ require 'flexmock'
5
+
6
+ module A
7
+ class B
8
+ def sample
9
+ :ab
10
+ end
11
+ end
12
+ end
13
+
14
+ class Bark
15
+ def listen
16
+ :woof
17
+ end
18
+ def Bark.identify
19
+ :barking
20
+ end
21
+ end
22
+
23
+ class Meow
24
+ def listen
25
+ :meow
26
+ end
27
+ end
28
+
29
+ class Dog
30
+ def bark
31
+ Bark.new
32
+ end
33
+ def wag
34
+ A::B.new
35
+ end
36
+ def bark_id
37
+ Bark.identify
38
+ end
39
+ end
40
+
41
+ class TestClassInterception < Test::Unit::TestCase
42
+ include FlexMock::TestCase
43
+
44
+ def test_original_functionality
45
+ dog = Dog.new
46
+ assert_equal :woof, dog.bark.listen
47
+ assert_equal :ab, dog.wag.sample
48
+ end
49
+
50
+ def test_interception
51
+ interceptor = intercept(Bark).in(Dog).with(Meow)
52
+ assert_kind_of FlexMock::ClassProxy, Dog::Bark
53
+ assert_equal Meow, Dog::Bark.proxied_class
54
+ dog = Dog.new
55
+ assert_equal :meow, dog.bark.listen
56
+ ensure
57
+ interceptor.restore if interceptor
58
+ end
59
+
60
+ # Since interception leaves a proxy class behind, we want to make
61
+ # sure interception works twice on the same object. So we repeat
62
+ # this test in full. We don't know which tests will _actually_ run
63
+ # first, but it doesn't matter as long as it is done twice.
64
+ def test_interception_repeated
65
+ test_interception
66
+ end
67
+
68
+ def test_interception_with_strings
69
+ interceptor = intercept("Bark").in("Dog").with("Meow")
70
+ assert_equal :meow, Dog.new.bark.listen
71
+ ensure
72
+ interceptor.restore if interceptor
73
+ end
74
+
75
+ def test_interception_with_symbols
76
+ interceptor = intercept(:Bark).in(:Dog).with(:Meow)
77
+ assert_equal :meow, Dog.new.bark.listen
78
+ ensure
79
+ interceptor.restore if interceptor
80
+ end
81
+
82
+ def test_interception_with_mixed_identifiers
83
+ interceptor = intercept(:Bark).in("Dog").with(Meow)
84
+ assert_equal :meow, Dog.new.bark.listen
85
+ ensure
86
+ interceptor.restore if interceptor
87
+ end
88
+
89
+ def test_interception_proxy_forward_other_methods
90
+ d = Dog.new
91
+ assert_equal :barking, d.bark_id
92
+
93
+ interceptor = intercept(:Bark).in("Dog").with(Meow)
94
+ interceptor.restore
95
+
96
+ d = Dog.new
97
+ assert_equal :barking, d.bark_id
98
+ end
99
+
100
+ end
101
+
102
+ class TestMockFactory < Test::Unit::TestCase
103
+ def test_mock_returns_factory
104
+ m = FlexMock.new("m")
105
+ f = m.mock_factory
106
+ assert_equal m, f.new
107
+ end
108
+ end
data/test/test_mock.rb CHANGED
@@ -38,7 +38,7 @@ class TestFlexMock < Test::Unit::TestCase
38
38
  ex = assert_raises(expected_error) {
39
39
  @mock.not_defined
40
40
  }
41
- assert_match /not_defined/, ex.message
41
+ assert_match(/not_defined/, ex.message)
42
42
  end
43
43
 
44
44
  def test_ignore_missing_method
@@ -116,7 +116,7 @@ class TestFlexMock < Test::Unit::TestCase
116
116
  xyz
117
117
  end
118
118
  }
119
- assert_match /undefined local variable or method/, ex.message
119
+ assert_match(/undefined local variable or method/, ex.message)
120
120
  end
121
121
 
122
122
  def test_sequential_values
data/test/test_naming.rb CHANGED
@@ -15,7 +15,7 @@ class TestNaming < Test::Unit::TestCase
15
15
  m.should_receive(:xx).with(1)
16
16
  m.xx(2)
17
17
  }
18
- assert_match /'mmm'/, ex.message
18
+ assert_match(/'mmm'/, ex.message)
19
19
  end
20
20
 
21
21
  def test_name_in_received_count_error
@@ -24,7 +24,7 @@ class TestNaming < Test::Unit::TestCase
24
24
  m.should_receive(:xx).once
25
25
  m.mock_verify
26
26
  }
27
- assert_match /'mmm'/, ex.message
27
+ assert_match(/'mmm'/, ex.message)
28
28
  end
29
29
 
30
30
  def test_naming_with_use
@@ -171,6 +171,26 @@ class TestFlexMockShoulds < Test::Unit::TestCase
171
171
  end
172
172
  end
173
173
 
174
+ def test_block_arg_given_to_no_args
175
+ FlexMock.use do |m|
176
+ m.should_receive(:hi).with_no_args.returns(20)
177
+ assert_failure {
178
+ m.hi { 1 }
179
+ }
180
+ end
181
+ end
182
+
183
+ def test_block_arg_given_to_matching_proc
184
+ FlexMock.use do |m|
185
+ arg = nil
186
+ m.should_receive(:hi).with(Proc).once.
187
+ and_return { |block| arg = block; block.call }
188
+ result = m.hi { 1 }
189
+ assert_equal 1, arg.call
190
+ assert_equal 1, result
191
+ end
192
+ end
193
+
174
194
  def test_arg_matching_precedence_when_best_first
175
195
  FlexMock.use("greeter") do |m|
176
196
  m.should_receive(:hi).with(1).once
@@ -6,10 +6,6 @@ require 'flexmock'
6
6
  class TestTuIntegrationMockVerificationInTeardown < Test::Unit::TestCase
7
7
  include FlexMock::TestCase
8
8
 
9
- def setup
10
- super
11
- end
12
-
13
9
  def teardown
14
10
  assert_raise(Test::Unit::AssertionFailedError) do
15
11
  super
@@ -38,9 +34,6 @@ end
38
34
  class TestTuIntegrationMockVerificationForgetfulSetup < Test::Unit::TestCase
39
35
  include FlexMock::TestCase
40
36
 
41
- def setup
42
- end
43
-
44
37
  def teardown
45
38
  assert_raise(Test::Unit::AssertionFailedError) do
46
39
  super
@@ -55,9 +48,21 @@ end
55
48
  class TestTuIntegrationSetupOverridenAndNoMocksOk < Test::Unit::TestCase
56
49
  include FlexMock::TestCase
57
50
 
58
- def setup
51
+ def test_mock_verification_occurs_during_teardown
59
52
  end
53
+ end
54
+
55
+ class TestTuIntegrationFailurePreventsVerification < Test::Unit::TestCase
56
+ include FlexMock::TestCase
60
57
 
61
58
  def test_mock_verification_occurs_during_teardown
59
+ flexmock('m').should_receive(:hi).once
60
+ simulate_failure
61
+ end
62
+
63
+ private
64
+
65
+ def simulate_failure
66
+ @test_passed = false
62
67
  end
63
68
  end
metadata CHANGED
@@ -1,10 +1,10 @@
1
1
  --- !ruby/object:Gem::Specification
2
- rubygems_version: 0.8.11.15
2
+ rubygems_version: 0.8.11
3
3
  specification_version: 1
4
4
  name: flexmock
5
5
  version: !ruby/object:Gem::Version
6
- version: 0.3.0
7
- date: 2006-05-14 00:00:00 -04:00
6
+ version: 0.3.1
7
+ date: 2006-06-17 00:00:00 -04:00
8
8
  summary: Simple and Flexible Mock Objects for Testing
9
9
  require_paths:
10
10
  - lib
@@ -25,7 +25,6 @@ required_ruby_version: !ruby/object:Gem::Version::Requirement
25
25
  platform: ruby
26
26
  signing_key:
27
27
  cert_chain:
28
- post_install_message:
29
28
  authors:
30
29
  - Jim Weirich
31
30
  files:
@@ -33,12 +32,13 @@ files:
33
32
  - Rakefile
34
33
  - README
35
34
  - lib/flexmock.rb
36
- - test/test_record_mode.rb
37
- - test/test_should_receive.rb
38
- - test/test_samples.rb
35
+ - test/test_class_interception.rb
36
+ - test/test_example.rb
39
37
  - test/test_mock.rb
40
38
  - test/test_naming.rb
41
- - test/test_example.rb
39
+ - test/test_record_mode.rb
40
+ - test/test_samples.rb
41
+ - test/test_should_receive.rb
42
42
  - test/test_tu_integration.rb
43
43
  - flexmock.blurb
44
44
  - install.rb