flexmock 0.4.5 → 0.5.0
Sign up to get free protection for your applications and to get access to all the features.
- data/CHANGELOG +4 -0
- data/README +33 -2
- data/Rakefile +8 -7
- data/doc/releases/flexmock-0.5.0.rdoc +103 -0
- data/lib/flexmock.rb +46 -7
- data/test/test_any_instance.rb +111 -0
- metadata +6 -3
data/CHANGELOG
CHANGED
data/README
CHANGED
@@ -3,7 +3,7 @@
|
|
3
3
|
FlexMock is a simple, but flexible, mock object library for Ruby unit
|
4
4
|
testing.
|
5
5
|
|
6
|
-
Version :: 0.
|
6
|
+
Version :: 0.5.0
|
7
7
|
|
8
8
|
= Links
|
9
9
|
|
@@ -283,6 +283,37 @@ everything is back to normal.
|
|
283
283
|
The stub technique was inspired by the +Stuba+ library in the +Mocha+
|
284
284
|
project.
|
285
285
|
|
286
|
+
=== Stubbing Behavior in All Instances Created by a Class Object
|
287
|
+
|
288
|
+
Sometimes you want to stub all instances created by a class object.
|
289
|
+
For example, you might wish to work with Connection objects that have
|
290
|
+
their "send" method stubbed out. However, the code under test creates
|
291
|
+
connections dynamically, so you can't stub them before the test is
|
292
|
+
run.
|
293
|
+
|
294
|
+
One approach is to stub the "new" method on the class object. The
|
295
|
+
stubbed implementation of "new" would create a mock object to be
|
296
|
+
returned as the value of "new". But since your stubbed implementation
|
297
|
+
of "new" has no access to the original behavior of new, you can't
|
298
|
+
really create stubs.
|
299
|
+
|
300
|
+
The <tt>any_instance</tt> method allows you to easily add stub
|
301
|
+
expectations to objects created by new. Here's the Connection example
|
302
|
+
using <tt>any_instance</tt>:
|
303
|
+
|
304
|
+
def test_connections
|
305
|
+
flexstub(Connection).any_instance do |new_con|
|
306
|
+
new_con.should_receive(:send).and_return(0)
|
307
|
+
end
|
308
|
+
connection = Connection.new
|
309
|
+
connection.send # This calls the stubbed version of send.
|
310
|
+
end
|
311
|
+
|
312
|
+
Note that FlexMock adds the stub expectations after the original +new+
|
313
|
+
method has completed. If the original version of +new+ yields the
|
314
|
+
newly created instance to a block, that block will get an unstubbed
|
315
|
+
version of the object.
|
316
|
+
|
286
317
|
=== Class Interception
|
287
318
|
|
288
319
|
<b>NOTE:</b> ::
|
@@ -450,7 +481,7 @@ following:
|
|
450
481
|
|
451
482
|
ruby-mock :: http://www.b13media.com/dev/ruby/mock.html
|
452
483
|
test-unit-mock :: http://www.deveiate.org/code/Test-Unit-Mock.shtml
|
453
|
-
|
484
|
+
mocha/stubba :: http://mocha.rubyforge.org/
|
454
485
|
== License
|
455
486
|
|
456
487
|
Copyright 2003, 2004, 2005, 2006 by Jim Weirich (jim@weirichhouse.org).
|
data/Rakefile
CHANGED
@@ -9,7 +9,7 @@ require 'rake/testtask'
|
|
9
9
|
CLEAN.include('*.tmp')
|
10
10
|
CLOBBER.include("html", 'pkg')
|
11
11
|
|
12
|
-
PKG_VERSION = '0.
|
12
|
+
PKG_VERSION = '0.5.0'
|
13
13
|
|
14
14
|
PKG_FILES = FileList[
|
15
15
|
'[A-Z]*',
|
@@ -41,18 +41,19 @@ end
|
|
41
41
|
|
42
42
|
# RDoc Target --------------------------------------------------------
|
43
43
|
|
44
|
-
|
44
|
+
task :rdoc => ["README"]
|
45
|
+
|
46
|
+
$rd = Rake::RDocTask.new("rdoc") do |rdoc|
|
45
47
|
rdoc.rdoc_dir = 'html'
|
46
48
|
rdoc.template = 'doc/jamis.rb'
|
47
|
-
# rdoc.template = 'html'
|
48
|
-
# rdoc.template = 'kilmer'
|
49
|
-
# rdoc.template = 'css2'
|
49
|
+
# rdoc.template = 'html'
|
50
|
+
# rdoc.template = 'kilmer'
|
51
|
+
# rdoc.template = 'css2'
|
50
52
|
rdoc.title = "Flex Mock"
|
51
53
|
rdoc.options << '--line-numbers' << '--inline-source' << '--main' << 'README'
|
52
54
|
rdoc.rdoc_files.include(RDOC_FILES)
|
53
55
|
end
|
54
56
|
|
55
|
-
task :rdoc => ["README"]
|
56
57
|
file "README" => ["Rakefile"] do
|
57
58
|
ruby %{-i.bak -pe 'sub!(/^Version *:: *(\\d+\\.)+\\d+ *$/, "Version :: #{PKG_VERSION}")' README} # "
|
58
59
|
end
|
@@ -95,7 +96,7 @@ else
|
|
95
96
|
#### Documentation and testing.
|
96
97
|
|
97
98
|
s.has_rdoc = true
|
98
|
-
s.extra_rdoc_files = rd.rdoc_files.reject { |fn| fn =~ /\.rb$/ }.to_a
|
99
|
+
s.extra_rdoc_files = $rd.rdoc_files.reject { |fn| fn =~ /\.rb$/ }.to_a
|
99
100
|
s.rdoc_options <<
|
100
101
|
'--title' << 'Flex Mock' <<
|
101
102
|
'--main' << 'README' <<
|
@@ -0,0 +1,103 @@
|
|
1
|
+
= FlexMock 0.5.0 Released
|
2
|
+
|
3
|
+
FlexMock is a flexible mocking library for use in Ruby's Test::Unit
|
4
|
+
test framework. Version 0.5.0 adds the ability to automatically stub any
|
5
|
+
instance created by an existing class.
|
6
|
+
|
7
|
+
== New in 0.5.0
|
8
|
+
|
9
|
+
* flexstub(obj) will now accept a block argument in the same way that
|
10
|
+
flexmock() does.
|
11
|
+
|
12
|
+
* When stubbing Class objects, the any_instance method can be used to
|
13
|
+
automatically stub any instance object created by the class. For
|
14
|
+
example, if you wish that any Connection object created during a test
|
15
|
+
has a stubbed "send" method, you could do the following:
|
16
|
+
|
17
|
+
def test_connections
|
18
|
+
flexstub(Connection).any_instance do |new_con|
|
19
|
+
new_con.should_receive(:send).and_return(0)
|
20
|
+
end
|
21
|
+
connection = Connection.new
|
22
|
+
connection.send # This calls the stubbed version of send.
|
23
|
+
end
|
24
|
+
|
25
|
+
Only objects created during the test will be automatically stubbed.
|
26
|
+
Existing objects are unaffected.
|
27
|
+
|
28
|
+
== What is FlexMock?
|
29
|
+
|
30
|
+
FlexMock is a flexible Ruby mocking library that works with Ruby's
|
31
|
+
Test::Unit framework to create easy to use mocks.
|
32
|
+
|
33
|
+
=== Features
|
34
|
+
|
35
|
+
* Easy integration with Test::Unit. Mocks created with the flexmock
|
36
|
+
method are automatically verified at the end of the test.
|
37
|
+
|
38
|
+
* A fluent interface that allows mock behavior to be specified very
|
39
|
+
easily.
|
40
|
+
|
41
|
+
* A "record mode" where an existing implementation can record its
|
42
|
+
interaction with a mock for later validation against a new
|
43
|
+
implementation.
|
44
|
+
|
45
|
+
* Easy mocking of individual methods in existing, non-mock objects.
|
46
|
+
|
47
|
+
=== Example
|
48
|
+
|
49
|
+
Suppose you had a Dog object that wagged a tail when it was happy.
|
50
|
+
Something like this:
|
51
|
+
|
52
|
+
class Dog
|
53
|
+
def initialize(a_tail)
|
54
|
+
@tail = a_tail
|
55
|
+
end
|
56
|
+
def happy
|
57
|
+
@tail.wag
|
58
|
+
end
|
59
|
+
end
|
60
|
+
|
61
|
+
To test the +Dog+ class without a real +Tail+ object (perhaps because
|
62
|
+
real +Tail+ objects activate servos in some robotic equipment), you
|
63
|
+
can do something like this:
|
64
|
+
|
65
|
+
require 'test/unit'
|
66
|
+
require 'flexmock'
|
67
|
+
|
68
|
+
class TestDog < Test::Unit::TestCase
|
69
|
+
include FlexMock::TestCase
|
70
|
+
|
71
|
+
def test_dog_wags_tail_when_happy
|
72
|
+
tail = flexmock("tail")
|
73
|
+
tail.should_receive(:wag).once
|
74
|
+
dog = Dog.new(tail)
|
75
|
+
dog.happy
|
76
|
+
end
|
77
|
+
end
|
78
|
+
|
79
|
+
FlexMock will automatically verify that the mocked tail object
|
80
|
+
received the message +wag+ exactly one time. If it doesn't, the test
|
81
|
+
will not pass.
|
82
|
+
|
83
|
+
See the FlexMock documentation at
|
84
|
+
http://onestepback.org/software/flexmock for details on specifying
|
85
|
+
arguments and return values on mocked methods, as well as a simple
|
86
|
+
technique for mocking tail objects when the Dog class creates the tail
|
87
|
+
objects directly.
|
88
|
+
|
89
|
+
== Availability
|
90
|
+
|
91
|
+
You can make sure you have the latest version with a quick RubyGems command:
|
92
|
+
|
93
|
+
gem install flexmock (you may need root/admin privileges)
|
94
|
+
|
95
|
+
Otherwise, you can get it from the more traditional places:
|
96
|
+
|
97
|
+
Download:: http://rubyforge.org/project/showfiles.php?group_id=170
|
98
|
+
|
99
|
+
You will find documentation at:
|
100
|
+
http://onestepback.org/software/flexmock/
|
101
|
+
|
102
|
+
-- Jim Weirich
|
103
|
+
|
data/lib/flexmock.rb
CHANGED
@@ -26,9 +26,9 @@ require 'test/unit'
|
|
26
26
|
#
|
27
27
|
# m = FlexMock.new("name")
|
28
28
|
# m.should_receive(:upcase).with("stuff").
|
29
|
-
#
|
29
|
+
# and_return("STUFF")
|
30
30
|
# m.should_receive(:downcase).with(String).
|
31
|
-
#
|
31
|
+
# and_return { |s| s.downcase }.once
|
32
32
|
#
|
33
33
|
# With Test::Unit Integration:
|
34
34
|
#
|
@@ -52,7 +52,7 @@ class FlexMock
|
|
52
52
|
class BadInterceptionError < RuntimeError; end
|
53
53
|
|
54
54
|
attr_reader :mock_name, :mock_groups
|
55
|
-
attr_accessor :mock_current_order
|
55
|
+
attr_accessor :mock_current_order, :mock_container
|
56
56
|
|
57
57
|
# Create a FlexMock object with the given name. The name is used in
|
58
58
|
# error messages.
|
@@ -61,6 +61,7 @@ class FlexMock
|
|
61
61
|
@expectations = Hash.new
|
62
62
|
@allocated_order = 0
|
63
63
|
@mock_current_order = 0
|
64
|
+
@mock_container = nil
|
64
65
|
@mock_groups = {}
|
65
66
|
@ignore_missing = false
|
66
67
|
@verified = false
|
@@ -843,6 +844,7 @@ class FlexMock
|
|
843
844
|
def flexmock_remember(mocking_object)
|
844
845
|
@flexmock_created_mocks ||= []
|
845
846
|
@flexmock_created_mocks << mocking_object
|
847
|
+
mocking_object.mock_container = self
|
846
848
|
mocking_object
|
847
849
|
end
|
848
850
|
end
|
@@ -977,6 +979,7 @@ class FlexMock
|
|
977
979
|
class StubProxy
|
978
980
|
attr_reader :mock
|
979
981
|
|
982
|
+
# Initialize a StubProxy object.
|
980
983
|
def initialize(obj, mock)
|
981
984
|
@obj = obj
|
982
985
|
@mock = mock
|
@@ -995,6 +998,32 @@ class FlexMock
|
|
995
998
|
@mock.should_receive(method_name)
|
996
999
|
end
|
997
1000
|
|
1001
|
+
# any_instance is a short cut method for overriding the behavior of any
|
1002
|
+
# instance created via a stubbed class object.
|
1003
|
+
def any_instance(&block)
|
1004
|
+
fail ArgumentError, "any_instance requires a Class to stub" unless Class === @obj
|
1005
|
+
self.should_receive(:new).and_return { |*args|
|
1006
|
+
new_obj = invoke_original(:new, args)
|
1007
|
+
mock = mock_container.flexstub(new_obj)
|
1008
|
+
block.call(mock)
|
1009
|
+
new_obj
|
1010
|
+
}
|
1011
|
+
nil
|
1012
|
+
end
|
1013
|
+
|
1014
|
+
# Invoke the original definition of method on the object supported by
|
1015
|
+
# the stub.
|
1016
|
+
def invoke_original(method, args)
|
1017
|
+
method_proc = @method_definitions[:new]
|
1018
|
+
block = nil
|
1019
|
+
if Proc === args.last
|
1020
|
+
block = args.last
|
1021
|
+
args = args[0...-1]
|
1022
|
+
end
|
1023
|
+
method_proc.call(*args, &block)
|
1024
|
+
end
|
1025
|
+
private :invoke_original
|
1026
|
+
|
998
1027
|
# Verify that the mock has been properly called. After verification,
|
999
1028
|
# detach the mocking infrastructure from the existing object.
|
1000
1029
|
def mock_verify
|
@@ -1012,6 +1041,18 @@ class FlexMock
|
|
1012
1041
|
@obj = nil
|
1013
1042
|
end
|
1014
1043
|
end
|
1044
|
+
|
1045
|
+
# Return the container for this mocking object. Returns nil if the
|
1046
|
+
# mock is not in a container. Mock containers make sure that mock objects
|
1047
|
+
# inside the container are torn down at the end of a test
|
1048
|
+
def mock_container
|
1049
|
+
@mock.mock_container
|
1050
|
+
end
|
1051
|
+
|
1052
|
+
# Set the container for this mock object.
|
1053
|
+
def mock_container=(container)
|
1054
|
+
@mock.mock_container = container
|
1055
|
+
end
|
1015
1056
|
|
1016
1057
|
private
|
1017
1058
|
|
@@ -1033,10 +1074,8 @@ class FlexMock
|
|
1033
1074
|
# not a singleton, all we need to do is override it with our own
|
1034
1075
|
# singleton.
|
1035
1076
|
def hide_existing_method(method_name)
|
1036
|
-
if
|
1037
|
-
|
1038
|
-
remove_current_method(method_name)
|
1039
|
-
end
|
1077
|
+
@method_definitions[method_name] = @obj.method(method_name) if @obj.respond_to?(method_name)
|
1078
|
+
remove_current_method(method_name) if singleton?(method_name)
|
1040
1079
|
define_proxy_method(method_name)
|
1041
1080
|
end
|
1042
1081
|
|
@@ -0,0 +1,111 @@
|
|
1
|
+
#!/usr/bin/env ruby
|
2
|
+
|
3
|
+
#---
|
4
|
+
# Copyright 2006 by Jim Weirich (jweirich@one.net).
|
5
|
+
# All rights reserved.
|
6
|
+
|
7
|
+
# Permission is granted for use, copying, modification, distribution,
|
8
|
+
# and distribution of modified versions of this work as long as the
|
9
|
+
# above copyright notice is included.
|
10
|
+
#+++
|
11
|
+
|
12
|
+
require 'test/unit'
|
13
|
+
require 'flexmock'
|
14
|
+
|
15
|
+
class TestStubbingOnNew < Test::Unit::TestCase
|
16
|
+
include FlexMock::TestCase
|
17
|
+
|
18
|
+
class Dog
|
19
|
+
def bark
|
20
|
+
:woof
|
21
|
+
end
|
22
|
+
def wag
|
23
|
+
:tail
|
24
|
+
end
|
25
|
+
end
|
26
|
+
|
27
|
+
class Cat
|
28
|
+
attr_reader :name
|
29
|
+
def initialize(name, &block)
|
30
|
+
@name = name
|
31
|
+
block.call(self) if block_given?
|
32
|
+
end
|
33
|
+
end
|
34
|
+
|
35
|
+
class Connection
|
36
|
+
def initialize(*args)
|
37
|
+
yield(self) if block_given?
|
38
|
+
end
|
39
|
+
def send(args)
|
40
|
+
post(args)
|
41
|
+
end
|
42
|
+
def post(args)
|
43
|
+
:unstubbed
|
44
|
+
end
|
45
|
+
end
|
46
|
+
|
47
|
+
def test_any_instance_allows_stubbing_of_existing_methods
|
48
|
+
flexstub(Dog).any_instance do |obj|
|
49
|
+
obj.should_receive(:bark).and_return(:whimper)
|
50
|
+
end
|
51
|
+
m = Dog.new
|
52
|
+
assert_equal :whimper, m.bark
|
53
|
+
end
|
54
|
+
|
55
|
+
def test_any_instance_stubs_still_have_existing_methods
|
56
|
+
flexstub(Dog).any_instance do |obj|
|
57
|
+
obj.should_receive(:bark).and_return(:whimper)
|
58
|
+
end
|
59
|
+
m = Dog.new
|
60
|
+
assert_equal :tail, m.wag
|
61
|
+
end
|
62
|
+
|
63
|
+
def test_any_instance_will_pass_args_to_new
|
64
|
+
flexstub(Cat).any_instance do |obj|
|
65
|
+
obj.should_receive(:meow).and_return(:scratch)
|
66
|
+
end
|
67
|
+
x = :not_called
|
68
|
+
m = Cat.new("Fido") { x = :called }
|
69
|
+
assert_equal :scratch, m.meow
|
70
|
+
assert_equal "Fido", m.name
|
71
|
+
assert_equal :called, x
|
72
|
+
end
|
73
|
+
|
74
|
+
def test_any_instance_stub_verification_happens_on_teardown
|
75
|
+
flexstub(Dog).any_instance do |obj|
|
76
|
+
obj.should_receive(:bark).once.and_return(nil)
|
77
|
+
end
|
78
|
+
|
79
|
+
fido = Dog.new
|
80
|
+
ex = assert_raise(Test::Unit::AssertionFailedError) { flexmock_teardown }
|
81
|
+
assert_match(/method 'bark\(.*\)' called incorrect number of times/, ex.message)
|
82
|
+
end
|
83
|
+
|
84
|
+
def test_any_instance_reports_error_on_non_classes
|
85
|
+
ex = assert_raise(ArgumentError) {
|
86
|
+
flexstub(Dog.new).any_instance do |obj|
|
87
|
+
obj.should_receive(:hi)
|
88
|
+
end
|
89
|
+
}
|
90
|
+
assert_match(/Class/, ex.message)
|
91
|
+
assert_match(/any_instance/, ex.message)
|
92
|
+
end
|
93
|
+
|
94
|
+
# Current behavior does not install stubs into the block passed to new.
|
95
|
+
# This is rather difficult to achieve, although it would be nice. For the
|
96
|
+
# moment, we assure that they are not stubbed, but I am willing to change
|
97
|
+
# this in the future.
|
98
|
+
def test_blocks_on_new_do_not_have_stubs_installed
|
99
|
+
flexstub(Connection).any_instance do |new_con|
|
100
|
+
new_con.should_receive(:post).and_return {
|
101
|
+
:stubbed
|
102
|
+
}
|
103
|
+
end
|
104
|
+
block_run = false
|
105
|
+
Connection.new do |c|
|
106
|
+
assert_equal :unstubbed, c.send("hi")
|
107
|
+
block_run = true
|
108
|
+
end
|
109
|
+
assert block_run
|
110
|
+
end
|
111
|
+
end
|
metadata
CHANGED
@@ -1,10 +1,10 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
|
-
rubygems_version: 0.9.
|
2
|
+
rubygems_version: 0.9.2
|
3
3
|
specification_version: 1
|
4
4
|
name: flexmock
|
5
5
|
version: !ruby/object:Gem::Version
|
6
|
-
version: 0.
|
7
|
-
date: 2007-
|
6
|
+
version: 0.5.0
|
7
|
+
date: 2007-02-10 00:00:00 -05:00
|
8
8
|
summary: Simple and Flexible Mock Objects for Testing
|
9
9
|
require_paths:
|
10
10
|
- lib
|
@@ -33,6 +33,7 @@ files:
|
|
33
33
|
- Rakefile
|
34
34
|
- README
|
35
35
|
- lib/flexmock.rb
|
36
|
+
- test/test_any_instance.rb
|
36
37
|
- test/test_class_interception.rb
|
37
38
|
- test/test_example.rb
|
38
39
|
- test/test_mock.rb
|
@@ -48,6 +49,7 @@ files:
|
|
48
49
|
- doc/releases/flexmock-0.4.1.rdoc
|
49
50
|
- doc/releases/flexmock-0.4.2.rdoc
|
50
51
|
- doc/releases/flexmock-0.4.3.rdoc
|
52
|
+
- doc/releases/flexmock-0.5.0.rdoc
|
51
53
|
test_files: []
|
52
54
|
|
53
55
|
rdoc_options:
|
@@ -63,6 +65,7 @@ extra_rdoc_files:
|
|
63
65
|
- doc/releases/flexmock-0.4.1.rdoc
|
64
66
|
- doc/releases/flexmock-0.4.2.rdoc
|
65
67
|
- doc/releases/flexmock-0.4.3.rdoc
|
68
|
+
- doc/releases/flexmock-0.5.0.rdoc
|
66
69
|
executables: []
|
67
70
|
|
68
71
|
extensions: []
|