flexmock 1.0.0.beta.4 → 1.0.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.
- data/README.rdoc +62 -175
- data/Rakefile +19 -1
- data/doc/GoogleExample.rdoc +57 -27
- data/doc/examples/rspec_examples_spec.rdoc +245 -0
- data/doc/examples/test_unit_examples_test.rdoc +241 -0
- data/doc/releases/flexmock-1.0.0.rdoc +2 -0
- data/lib/flexmock/spy_describers.rb +1 -1
- data/lib/flexmock/version.rb +0 -2
- data/test/assert_spy_called_test.rb +3 -3
- data/test/rspec_integration/spy_example_spec.rb +6 -6
- data/test/spys_test.rb +15 -0
- metadata +9 -5
data/README.rdoc
CHANGED
@@ -1,16 +1,16 @@
|
|
1
|
-
= Flex Mock -- Making
|
1
|
+
= Flex Mock -- Making Mocking Easy
|
2
2
|
|
3
3
|
FlexMock is a simple, but flexible, mock object library for Ruby unit
|
4
4
|
testing.
|
5
5
|
|
6
|
-
Version :: 1.0.0
|
6
|
+
Version :: 1.0.0
|
7
7
|
|
8
8
|
= Links
|
9
9
|
|
10
|
-
<b>Documents</b>
|
11
|
-
<b>RubyGems</b>
|
12
|
-
<b>Source</b>
|
13
|
-
<b>Bug Reports
|
10
|
+
<b>Documents</b> :: http://flexmock.rubyforge.org
|
11
|
+
<b>RubyGems</b> :: Install with: <b>gem install flexmock</b>
|
12
|
+
<b>Source</b> :: https://github.com/jimweirich/flexmock
|
13
|
+
<b>Bug Reports / Issue Tracking</b> :: https://github.com/jimweirich/flexmock/issues
|
14
14
|
|
15
15
|
== Installation
|
16
16
|
|
@@ -181,10 +181,11 @@ FlexMock::MockContainer#flexmock for more details.
|
|
181
181
|
|
182
182
|
* should_receive
|
183
183
|
* new_instances
|
184
|
-
*
|
185
|
-
*
|
186
|
-
*
|
187
|
-
*
|
184
|
+
* flexmock_get
|
185
|
+
* flexmock_teardown
|
186
|
+
* flexmock_verify
|
187
|
+
* flexmock_received?
|
188
|
+
* flexmock_calls
|
188
189
|
|
189
190
|
* <b>mock = flexmock(...) { |mock| mock.should_receive(...) }</b>
|
190
191
|
|
@@ -664,6 +665,7 @@ library in the <code>Mocha</code> project.
|
|
664
665
|
|
665
666
|
FlexMock supports spy-like mocks as well as the traditional mocks.
|
666
667
|
|
668
|
+
# In Test::Unit / MiniTest
|
667
669
|
class TestDogBarking < Test::Unit::TestCase
|
668
670
|
def test_dog
|
669
671
|
dog = flexmock(:on, Dog)
|
@@ -672,6 +674,44 @@ FlexMock supports spy-like mocks as well as the traditional mocks.
|
|
672
674
|
end
|
673
675
|
end
|
674
676
|
|
677
|
+
# In RSpec
|
678
|
+
describe Dog do
|
679
|
+
let(:dog) { flexmock(:on, Dog) }
|
680
|
+
it "barks loudly" do
|
681
|
+
dog.bark("loud")
|
682
|
+
dog.should have_received(:bark).with("loud")
|
683
|
+
end
|
684
|
+
end
|
685
|
+
|
686
|
+
Since spies are verified after the code under test is run, they fit
|
687
|
+
very nicely with the Given/When/Then technique of specification. Here
|
688
|
+
is the above RSpec example using the rspec-given gem:
|
689
|
+
|
690
|
+
require 'rspec/given'
|
691
|
+
|
692
|
+
describe Dog do
|
693
|
+
Given(:dog) { flexmock(:on, Dog) }
|
694
|
+
|
695
|
+
context "when barking loudly" do
|
696
|
+
When { dog.bark("loud") }
|
697
|
+
Then { dog.should have_received(:bark).with("loud") }
|
698
|
+
end
|
699
|
+
end
|
700
|
+
|
701
|
+
*NOTE:* You can only spy on methods that are mocked or stubbed. That's
|
702
|
+
not a problem with regular mocks, but normal methods on partial
|
703
|
+
objects will not be recorded.
|
704
|
+
|
705
|
+
You can get around this limitation by stubbing the method in question
|
706
|
+
on the normal mock, and then specifying <code>pass_thru</code>.
|
707
|
+
Assuming <code.:bark</code> is a normal method on a Dog object, then
|
708
|
+
the following allows for spying on <code>:bark</code>.
|
709
|
+
|
710
|
+
dog = Dog.new
|
711
|
+
flexmock(dog).should_receive(:bark).pass_thru
|
712
|
+
# ...
|
713
|
+
dog.should have_received(:bark)
|
714
|
+
|
675
715
|
==== Asserting Spy Methods are Called (Test::Unit / MiniTest)
|
676
716
|
|
677
717
|
FlexMock provied a custom assertion method for use with Test::Unit and
|
@@ -909,173 +949,14 @@ beforehand.
|
|
909
949
|
|
910
950
|
== Examples
|
911
951
|
|
912
|
-
|
913
|
-
|
914
|
-
require 'flexmock/test_unit'
|
915
|
-
|
916
|
-
class TestSimple < Test::Unit::TestCase
|
917
|
-
def test_simple_mock
|
918
|
-
m = flexmock(:pi => 3.1416, :e => 2.71)
|
919
|
-
assert_equal 3.1416, m.pi
|
920
|
-
assert_equal 2.71, m.e
|
921
|
-
end
|
922
|
-
end
|
923
|
-
|
924
|
-
=== Create a mock object that returns an undefined object for method calls
|
925
|
-
|
926
|
-
require 'flexmock/test_unit'
|
927
|
-
|
928
|
-
class TestUndefined < Test::Unit::TestCase
|
929
|
-
def test_undefined_values
|
930
|
-
m = flexmock("mock")
|
931
|
-
m.should_receive(:divide_by).with(0).
|
932
|
-
and_return_undefined
|
933
|
-
assert_equal FlexMock.undefined, m.divide_by(0)
|
934
|
-
end
|
935
|
-
end
|
936
|
-
|
937
|
-
=== Expect multiple queries and a single update
|
938
|
-
|
939
|
-
Multiple calls to the query method will be allows, and calls may have any
|
940
|
-
argument list. Each call to query will return the three element array [1, 2,
|
941
|
-
3]. The call to update must have a specific argument of 5.
|
942
|
-
|
943
|
-
require 'flexmock/test_unit'
|
944
|
-
|
945
|
-
class TestDb < Test::Unit::TestCase
|
946
|
-
def test_db
|
947
|
-
db = flexmock('db')
|
948
|
-
db.should_receive(:query).and_return([1,2,3])
|
949
|
-
db.should_receive(:update).with(5).and_return(nil).once
|
950
|
-
# test code here
|
951
|
-
end
|
952
|
-
end
|
953
|
-
|
954
|
-
=== Expect all queries before any updates
|
955
|
-
|
956
|
-
(This and following examples assume that the 'flexmock/test_unit' file has
|
957
|
-
been required.)
|
958
|
-
|
959
|
-
All the query message must occur before any of the update messages.
|
960
|
-
|
961
|
-
def test_query_and_update
|
962
|
-
db = flexmock('db')
|
963
|
-
db.should_receive(:query).and_return([1,2,3]).ordered
|
964
|
-
db.should_receive(:update).and_return(nil).ordered
|
965
|
-
# test code here
|
966
|
-
end
|
967
|
-
|
968
|
-
=== Expect several queries with different parameters
|
969
|
-
|
970
|
-
The queries should happen after startup but before finish. The
|
971
|
-
queries themselves may happen in any order (because they are in the
|
972
|
-
same order group). The first two queries should happen exactly once,
|
973
|
-
but the third query (which matches any query call with a four
|
974
|
-
character parameter) may be called multiple times (but at least once).
|
975
|
-
Startup and finish must also happen exactly once.
|
976
|
-
|
977
|
-
Also note that we use the <code>with</code> method to match different
|
978
|
-
argument values to figure out what value to return.
|
979
|
-
|
980
|
-
def test_ordered_queries
|
981
|
-
db = flexmock('db')
|
982
|
-
db.should_receive(:startup).once.ordered
|
983
|
-
db.should_receive(:query).with("CPWR").and_return(12.3).
|
984
|
-
once.ordered(:queries)
|
985
|
-
db.should_receive(:query).with("MSFT").and_return(10.0).
|
986
|
-
once.ordered(:queries)
|
987
|
-
db.should_receive(:query).with(/^....$/).and_return(3.3).
|
988
|
-
at_least.once.ordered(:queries)
|
989
|
-
db.should_receive(:finish).once.ordered
|
990
|
-
# test code here
|
991
|
-
end
|
992
|
-
|
993
|
-
=== Same as above, but using the Record Mode interface
|
994
|
-
|
995
|
-
The record mode interface offers much the same features as the
|
996
|
-
<code>should_receive</code> interface introduced so far, but it allows
|
997
|
-
the messages to be sent directly to a recording object rather than be
|
998
|
-
specified indirectly using a symbol.
|
999
|
-
|
1000
|
-
def test_ordered_queries_in_record_mode
|
1001
|
-
db = flexmock('db')
|
1002
|
-
db.should_expect do |rec|
|
1003
|
-
rec.startup.once.ordered
|
1004
|
-
rec.query("CPWR") { 12.3 }.once.ordered(:queries)
|
1005
|
-
rec.query("MSFT") { 10.0 }.once.ordered(:queries)
|
1006
|
-
rec.query(/^....$/) { 3.3 }.at_least.once.ordered(:queries)
|
1007
|
-
rec.finish.once.ordered
|
1008
|
-
end
|
1009
|
-
# test code here using +db+.
|
1010
|
-
end
|
1011
|
-
|
1012
|
-
=== Using Record Mode to record a known, good algorithm for testing
|
1013
|
-
|
1014
|
-
Record mode is nice when you have a known, good algorithm that can use
|
1015
|
-
a recording mock object to record the steps. Then you compare the
|
1016
|
-
execution of a new algorithm to behavior of the old using the recorded
|
1017
|
-
expectations in the mock. For this you probably want to put the
|
1018
|
-
recorder in _strict_ mode so that the recorded expectations use exact
|
1019
|
-
matching on argument lists, and strict ordering of the method calls.
|
1020
|
-
|
1021
|
-
<b>Note:</b> This is most useful when there are no queries on the mock
|
1022
|
-
objects, because the query responses cannot be programmed into the
|
1023
|
-
recorder object.
|
1024
|
-
|
1025
|
-
def test_build_xml
|
1026
|
-
builder = flexmock('builder')
|
1027
|
-
builder.should_expect do |rec|
|
1028
|
-
rec.should_be_strict
|
1029
|
-
known_good_way_to_build_xml(rec) # record the messages
|
1030
|
-
end
|
1031
|
-
new_way_to_build_xml(builder) # compare to new way
|
1032
|
-
end
|
1033
|
-
|
1034
|
-
=== Expect multiple calls, returning a different value each time
|
1035
|
-
|
1036
|
-
Sometimes you need to return different values for each call to a
|
1037
|
-
mocked method. This example shifts values out of a list for this
|
1038
|
-
effect.
|
1039
|
-
|
1040
|
-
def test_multiple_gets
|
1041
|
-
file = flexmock('file')
|
1042
|
-
file.should_receive(:gets).with_no_args.
|
1043
|
-
and_return("line 1\n", "line 2\n")
|
1044
|
-
# test code here
|
1045
|
-
end
|
1046
|
-
|
1047
|
-
=== Ignore uninteresting messages
|
1048
|
-
|
1049
|
-
Generally you need to mock only those methods that return an
|
1050
|
-
interesting value or wish to assert were sent in a particular manner.
|
1051
|
-
Use the <code>should_ignore_missing</code> method to turn on missing
|
1052
|
-
method ignoring.
|
1053
|
-
|
1054
|
-
def test_an_important_message
|
1055
|
-
m = flexmock('m')
|
1056
|
-
m.should_receive(:an_important_message).and_return(1).once
|
1057
|
-
m.should_ignore_missing
|
1058
|
-
# test code here
|
1059
|
-
end
|
952
|
+
Refer to the following documents for examples of using FlexMock:
|
1060
953
|
|
1061
|
-
|
1062
|
-
|
1063
|
-
undefined object will return the undefined object.
|
954
|
+
* {RSpec Examples}[link:doc/examples/rspec_examples_spec_rdoc.html]
|
955
|
+
* {Test::Unit / MiniTest Examples}[link:doc/examples/test_unit_examples_test_rdoc.html]
|
1064
956
|
|
1065
|
-
|
1066
|
-
|
1067
|
-
|
1068
|
-
to a quote service via a web service. Since we don't want to use a
|
1069
|
-
real web service in our unit tests, we will mock the quote service.
|
1070
|
-
|
1071
|
-
def test_portfolio_value
|
1072
|
-
flexmock(QuoteService).new_instances do |m|
|
1073
|
-
m.should_receive(:quote).and_return(100)
|
1074
|
-
end
|
1075
|
-
port = Portfolio.new
|
1076
|
-
value = port.value # Portfolio calls QuoteService.quote
|
1077
|
-
assert_equal 100, value
|
1078
|
-
end
|
957
|
+
*NOTE:* <em>If the above links are not working, you probably need to
|
958
|
+
read the documents from the {documentation
|
959
|
+
site}[http://flexmock.rubyforge.org]</em>
|
1079
960
|
|
1080
961
|
== License
|
1081
962
|
|
@@ -1091,6 +972,12 @@ above copyright notice is included.
|
|
1091
972
|
Author:: Jim Weirich <jim.weirich@gmail.com>
|
1092
973
|
Requires:: Ruby 1.9.2 or later (Earlier versions of Flexmock may work with earlier versions of Ruby)
|
1093
974
|
|
975
|
+
== See Also
|
976
|
+
|
977
|
+
If you like the spy capability of FlexMock, you should check out the
|
978
|
+
rspec-given gem (http://rubygems.org/gems/rspec-given) that allows you
|
979
|
+
to use Given/When/Then statements in you specifications.
|
980
|
+
|
1094
981
|
== Warranty
|
1095
982
|
|
1096
983
|
This software is provided "as is" and without any express or
|
data/Rakefile
CHANGED
@@ -23,6 +23,11 @@ load './lib/flexmock/version.rb'
|
|
23
23
|
|
24
24
|
PKG_VERSION = FlexMock::VERSION
|
25
25
|
|
26
|
+
EXAMPLE_RB = FileList['doc/examples/*.rb']
|
27
|
+
EXAMPLE_DOC = EXAMPLE_RB.ext('rdoc')
|
28
|
+
|
29
|
+
CLOBBER.include(EXAMPLE_DOC)
|
30
|
+
|
26
31
|
PKG_FILES = FileList[
|
27
32
|
'[A-Z]*',
|
28
33
|
'lib/**/*.rb',
|
@@ -36,7 +41,7 @@ RDOC_FILES = FileList[
|
|
36
41
|
'CHANGES',
|
37
42
|
'lib/**/*.rb',
|
38
43
|
'doc/**/*.rdoc',
|
39
|
-
]
|
44
|
+
] + EXAMPLE_DOC
|
40
45
|
|
41
46
|
task :default => [:test_all, :rspec]
|
42
47
|
task :test_all => [:test]
|
@@ -84,6 +89,19 @@ file "html/index.html" => ["Rakefile"] + RDOC_FILES do
|
|
84
89
|
sh "rdoc -o html --title FlexMock --line-numbers -m README.rdoc #{RDOC_FILES}"
|
85
90
|
end
|
86
91
|
|
92
|
+
EXAMPLE_RB.zip(EXAMPLE_DOC).each do |source, target|
|
93
|
+
file target => source do
|
94
|
+
open(source, "r") do |ins|
|
95
|
+
open(target, "w") do |outs|
|
96
|
+
outs.puts "= FlexMock Examples"
|
97
|
+
ins.each do |line|
|
98
|
+
outs.puts " #{line}"
|
99
|
+
end
|
100
|
+
end
|
101
|
+
end
|
102
|
+
end
|
103
|
+
end
|
104
|
+
|
87
105
|
file "README.rdoc" => ["Rakefile", "lib/flexmock/version.rb"] do
|
88
106
|
ruby %{-i.bak -pe '$_.sub!(/^Version *:: *((\\d+|beta|rc)\\.)+\\d+ *$/i, "Version :: #{PKG_VERSION}")' README.rdoc} # "
|
89
107
|
end
|
data/doc/GoogleExample.rdoc
CHANGED
@@ -1,6 +1,8 @@
|
|
1
1
|
= Extended FlexMock Example Using Google4R
|
2
2
|
|
3
|
-
Google4R is a simple Ruby wrapper around the Google APIs.
|
3
|
+
Google4R is a simple Ruby wrapper around the Google APIs. In this
|
4
|
+
extended example, we will use FlexMock to test software that uses the
|
5
|
+
Google APIs, without every communicating with Google itself.
|
4
6
|
|
5
7
|
== Purchase.rb
|
6
8
|
|
@@ -38,9 +40,11 @@ talking to the Google APIs. The config object given to the Purchase
|
|
38
40
|
initializer is simply a hash of values defining the merchant_id, merchant_key
|
39
41
|
and sandbox flag. To use the real Google checkout APIs, you will need to
|
40
42
|
obtains a merchant id and key from Google. Since we will be mocking the Google
|
41
|
-
interaction, we can use dummy values in our test.
|
43
|
+
interaction, we can use dummy values in our test.
|
42
44
|
|
43
|
-
The tax table factory is required by the Google4R software.
|
45
|
+
The tax table factory is required by the Google4R software. We provide
|
46
|
+
the following simplified one. Read the Google API documents for more
|
47
|
+
information.
|
44
48
|
|
45
49
|
class TestTaxTableFactory
|
46
50
|
def effective_tax_tables_at(time)
|
@@ -49,12 +53,14 @@ The tax table factory is required by the Google4R software. We provide the foll
|
|
49
53
|
tax_free_table.create_rule do |rule|
|
50
54
|
rule.area = UsCountryArea.new(UsCountryArea::ALL)
|
51
55
|
rule.rate = 0.0
|
52
|
-
end
|
56
|
+
end
|
53
57
|
return [tax_free_table]
|
54
58
|
end
|
55
59
|
end
|
56
60
|
|
57
|
-
+Item+ is simply an ActiveRecord class that we are using to hold our
|
61
|
+
+Item+ is simply an ActiveRecord class that we are using to hold our
|
62
|
+
purchase item information. It should respond to the +name+,
|
63
|
+
+description+ and +unit_price+ messages.
|
58
64
|
|
59
65
|
== Testing Without Using External Resources
|
60
66
|
|
@@ -108,15 +114,15 @@ Here is the complete unit test:
|
|
108
114
|
:name => "Deschutes",
|
109
115
|
:description => "Deschutes model Guitar",
|
110
116
|
:unit_price => Money.new(2400.00)))
|
111
|
-
|
117
|
+
|
112
118
|
flexmock(Google4R::Checkout::CheckoutCommand).new_instances do |instance|
|
113
119
|
instance.should_receive(:send_to_google_checkout).once.
|
114
120
|
and_return(flexmock(:redirect_url => "http://google.response.url"))
|
115
121
|
end
|
116
122
|
|
117
123
|
# Execute
|
118
|
-
p = Purchase.new({
|
119
|
-
:merchant_id => 'dummy_id',
|
124
|
+
p = Purchase.new({
|
125
|
+
:merchant_id => 'dummy_id',
|
120
126
|
:merchant_key => 'dummy_key',
|
121
127
|
:use_sandbox => true })
|
122
128
|
url = p.purchase(1)
|
@@ -124,27 +130,37 @@ Here is the complete unit test:
|
|
124
130
|
# Assert
|
125
131
|
assert_equal "http://google.response.url", url
|
126
132
|
end
|
127
|
-
|
133
|
+
|
128
134
|
== Testing the Details
|
129
135
|
|
130
|
-
The above test is fine as far as it goes. It demonstrates how to use
|
131
|
-
avoid talking to external resources such as databases and web
|
136
|
+
The above test is fine as far as it goes. It demonstrates how to use
|
137
|
+
mocks to avoid talking to external resources such as databases and web
|
138
|
+
services. But as a unit test, it is sorely lacking in several areas.
|
132
139
|
|
133
|
-
All the test really demonstrates is that the +send_to_google_checkout+
|
140
|
+
All the test really demonstrates is that the +send_to_google_checkout+
|
141
|
+
method is called. There are no tests to ensure that the right item
|
142
|
+
descriptions and prices are correctly stored in the cart. In fact, if
|
143
|
+
we rewrote the purchase method as follows:
|
134
144
|
|
135
145
|
def purchase(item_id, quantity=1)
|
136
146
|
@frontend.create_checkout_command.send_to_google_checkout.redirect_url
|
137
|
-
end
|
147
|
+
end
|
138
148
|
|
139
|
-
it would still pass the unit test we designed, even though the rewrite
|
149
|
+
it would still pass the unit test we designed, even though the rewrite
|
150
|
+
is obviously an incorrect implementation.
|
140
151
|
|
141
152
|
A more complete test is a bit more complicated. Here are the details.
|
142
153
|
|
143
154
|
=== Mocking Active Record
|
144
155
|
|
145
|
-
Our incorrect version of purchase never calls the +find+ method of
|
156
|
+
Our incorrect version of purchase never calls the +find+ method of
|
157
|
+
Item. We can easily test for that by adding a +once+ constraint one
|
158
|
+
that mock specification. Since find is a read-only method, we don't
|
159
|
+
really care if it is called multiple times, as long as it is called at
|
160
|
+
least one time, so we will add an +at_least+ modifier as well.
|
146
161
|
|
147
|
-
Finally, we are going to break the guitar mock out into its own
|
162
|
+
Finally, we are going to break the guitar mock out into its own
|
163
|
+
declaration. The reason will become obvious in a bit.
|
148
164
|
|
149
165
|
mock_guitar = flexmock("guitar",
|
150
166
|
:name => "Deschutes",
|
@@ -156,11 +172,19 @@ Finally, we are going to break the guitar mock out into its own declaration. Th
|
|
156
172
|
|
157
173
|
=== Mocking a Cart Item
|
158
174
|
|
159
|
-
The next bit is a wee bit complicated, but we will handle it a little
|
175
|
+
The next bit is a wee bit complicated, but we will handle it a little
|
176
|
+
bit at a time so that it doesn't become overwhelming.
|
160
177
|
|
161
|
-
There are three main objects in the Google checkout API that we deal
|
178
|
+
There are three main objects in the Google checkout API that we deal
|
179
|
+
with in the next section.: (1) the checkout command object returned by
|
180
|
+
the front end, (2) the cart object returned by the checkout command,
|
181
|
+
and (3) the item passed to the block in the +create_item+ call.
|
162
182
|
|
163
|
-
We will tackle them in reverse order, starting with the item objects
|
183
|
+
We will tackle them in reverse order, starting with the item objects
|
184
|
+
given to the +create_item+ block. The item must respond to four
|
185
|
+
attribute assignments. This is straightforward to mock, just make sure
|
186
|
+
you include the +once+ constraint so that the assignments are
|
187
|
+
required.
|
164
188
|
|
165
189
|
mock_item = flexmock("item")
|
166
190
|
mock_item.should_receive(:name=).with(mock_guitar.name).once
|
@@ -168,18 +192,24 @@ We will tackle them in reverse order, starting with the item objects given to th
|
|
168
192
|
mock_item.should_receive(:unit_price=).with(mock_guitar.unit_price).once
|
169
193
|
mock_item.should_receive(:quantity=).with(1).once
|
170
194
|
|
171
|
-
Notice how we used the mock_guitar object defined earlier to provide
|
195
|
+
Notice how we used the mock_guitar object defined earlier to provide
|
196
|
+
values in the +with+ constraint. This way we don't have to repeat the
|
197
|
+
explicit strings and values we are checking. (Keep it DRY!).
|
172
198
|
|
173
199
|
=== Mocking the Cart
|
174
200
|
|
175
|
-
The mock cart object will pass the mock_item to a block when the
|
201
|
+
The mock cart object will pass the mock_item to a block when the
|
202
|
+
+create_item+ method is called. We specify that with the following:
|
176
203
|
|
177
204
|
mock_cart = flexmock("cart")
|
178
205
|
mock_cart.should_receive(:create_item).with(Proc).once.and_return { |block|
|
179
206
|
block.call(mock_item)
|
180
207
|
}
|
181
208
|
|
182
|
-
FlexMock objects can handle blocks passed to them by treating them as
|
209
|
+
FlexMock objects can handle blocks passed to them by treating them as
|
210
|
+
the final object in the calling list. Use +Proc+ in the +with+
|
211
|
+
constraint to match the block and then invoke the block explicitly via
|
212
|
+
<tt>block.call(...)</tt> in the +and_return+ specification.
|
183
213
|
|
184
214
|
=== Mocking the Checkout Command
|
185
215
|
|
@@ -223,8 +253,8 @@ Here is the complete detailed version of the test method.
|
|
223
253
|
end
|
224
254
|
|
225
255
|
# Execute
|
226
|
-
p = Purchase.new({
|
227
|
-
:merchant_id => 'dummy_id',
|
256
|
+
p = Purchase.new({
|
257
|
+
:merchant_id => 'dummy_id',
|
228
258
|
:merchant_key => 'dummy_key',
|
229
259
|
:use_sandbox => true })
|
230
260
|
url = p.purchase(1)
|
@@ -253,9 +283,9 @@ Perhaps, but using mock objects have several definite advantages:
|
|
253
283
|
your code correctly handle the case where you get an exception when
|
254
284
|
connecting to google? Mocks can easily create those error conditions that
|
255
285
|
are difficult to achieve with real objects.
|
256
|
-
|
286
|
+
|
257
287
|
E.g.
|
258
|
-
|
288
|
+
|
259
289
|
instance.should_receive(:send_to_google_checkout).once.
|
260
290
|
and_return { raise Google4R::Checkout::GoogleCheckoutError }
|
261
291
|
|
@@ -272,4 +302,4 @@ expect it to. We don't actually care about the Google4R software itself in
|
|
272
302
|
this test case (presumably we do have tests that cover Google4R, but those are
|
273
303
|
different tests).
|
274
304
|
|
275
|
-
In the end, mock objects are a
|
305
|
+
In the end, mock objects are a powerful tool to have in your testing toolbox.
|
@@ -0,0 +1,245 @@
|
|
1
|
+
= FlexMock Examples
|
2
|
+
RSpec.configure do |config|
|
3
|
+
config.mock_with :flexmock
|
4
|
+
end
|
5
|
+
|
6
|
+
describe "Simple Spec" do
|
7
|
+
|
8
|
+
# Simple stubbing of some methods
|
9
|
+
|
10
|
+
it "stubs a couple of methods" do
|
11
|
+
m = flexmock(:pi => 3.1416, :e => 2.71)
|
12
|
+
m.pi.should == 3.1416
|
13
|
+
m.e.should == 2.71
|
14
|
+
end
|
15
|
+
end
|
16
|
+
|
17
|
+
|
18
|
+
describe "Returning Undefined" do
|
19
|
+
|
20
|
+
# Create a mock object that returns an undefined object for method calls
|
21
|
+
|
22
|
+
it "returns undefined values" do
|
23
|
+
m = flexmock("mock")
|
24
|
+
m.should_receive(:divide_by).with(0).
|
25
|
+
and_return_undefined
|
26
|
+
|
27
|
+
m.divide_by(0).should == FlexMock.undefined
|
28
|
+
end
|
29
|
+
end
|
30
|
+
|
31
|
+
describe "Multiple Queries and Single Updates" do
|
32
|
+
|
33
|
+
# Expect multiple queries and a single update
|
34
|
+
|
35
|
+
# Multiple calls to the query method will be allows, and calls may
|
36
|
+
# have any argument list. Each call to query will return the three
|
37
|
+
# element array [1, 2, 3]. The call to update must have a specific
|
38
|
+
# argument of 5.
|
39
|
+
|
40
|
+
it "queries the db" do
|
41
|
+
db = flexmock('db')
|
42
|
+
db.should_receive(:query).and_return([1,2,3])
|
43
|
+
db.should_receive(:update).with(5).and_return(nil).once
|
44
|
+
|
45
|
+
# Test Code
|
46
|
+
|
47
|
+
db.query
|
48
|
+
db.update(5)
|
49
|
+
end
|
50
|
+
end
|
51
|
+
|
52
|
+
describe "Ordered Mocks" do
|
53
|
+
|
54
|
+
# Expect all queries before any updates
|
55
|
+
|
56
|
+
# All the query message must occur before any of the update
|
57
|
+
# messages.
|
58
|
+
|
59
|
+
it "queries and updates the database" do
|
60
|
+
db = flexmock('db')
|
61
|
+
db.should_receive(:query).and_return([1,2,3]).ordered
|
62
|
+
db.should_receive(:update).and_return(nil).ordered
|
63
|
+
|
64
|
+
# test code here
|
65
|
+
|
66
|
+
db.query
|
67
|
+
db.update
|
68
|
+
end
|
69
|
+
end
|
70
|
+
|
71
|
+
describe "Ordered Mocks" do
|
72
|
+
|
73
|
+
# Expect several queries with different parameters
|
74
|
+
|
75
|
+
# The queries should happen after startup but before finish. The
|
76
|
+
# queries themselves may happen in any order (because they are in
|
77
|
+
# the same order group). The first two queries should happen exactly
|
78
|
+
# once, but the third query (which matches any query call with a
|
79
|
+
# four character parameter) may be called multiple times (but at
|
80
|
+
# least once). Startup and finish must also happen exactly once.
|
81
|
+
|
82
|
+
# Also note that we use the <code>with</code> method to match
|
83
|
+
# different argument values to figure out what value to return.
|
84
|
+
|
85
|
+
it "queries the database in a particular order" do
|
86
|
+
db = flexmock('db')
|
87
|
+
db.should_receive(:startup).once.ordered
|
88
|
+
db.should_receive(:query).with("CPWR").and_return(12.3).
|
89
|
+
once.ordered(:queries)
|
90
|
+
db.should_receive(:query).with("MSFT").and_return(10.0).
|
91
|
+
once.ordered(:queries)
|
92
|
+
db.should_receive(:query).with(/^....$/).and_return(3.3).
|
93
|
+
at_least.once.ordered(:queries)
|
94
|
+
db.should_receive(:finish).once.ordered
|
95
|
+
|
96
|
+
# Test Code
|
97
|
+
|
98
|
+
db.startup
|
99
|
+
db.query("MSFT")
|
100
|
+
db.query("XYZY")
|
101
|
+
db.query("CPWR")
|
102
|
+
db.finish
|
103
|
+
end
|
104
|
+
end
|
105
|
+
|
106
|
+
describe "Ordered Mocks" do
|
107
|
+
|
108
|
+
# Same as above, but using the Record Mode interface
|
109
|
+
|
110
|
+
# The record mode interface offers much the same features as the
|
111
|
+
# <code>should_receive</code> interface introduced so far, but it
|
112
|
+
# allows the messages to be sent directly to a recording object
|
113
|
+
# rather than be specified indirectly using a symbol.
|
114
|
+
|
115
|
+
|
116
|
+
it "records the queries for replay" do
|
117
|
+
db = flexmock('db')
|
118
|
+
db.should_expect do |rec|
|
119
|
+
rec.startup.once.ordered
|
120
|
+
rec.query("CPWR") { 12.3 }.once.ordered(:queries)
|
121
|
+
rec.query("MSFT") { 10.0 }.once.ordered(:queries)
|
122
|
+
rec.query(/^....$/) { 3.3 }.at_least.once.ordered(:queries)
|
123
|
+
rec.finish.once.ordered
|
124
|
+
end
|
125
|
+
|
126
|
+
# Test Code
|
127
|
+
|
128
|
+
db.startup
|
129
|
+
db.query("MSFT")
|
130
|
+
db.query("XYZY")
|
131
|
+
db.query("CPWR")
|
132
|
+
db.finish
|
133
|
+
end
|
134
|
+
end
|
135
|
+
|
136
|
+
describe "Record Mode" do
|
137
|
+
|
138
|
+
# Using Record Mode to record a known, good algorithm for testing
|
139
|
+
|
140
|
+
# Record mode is nice when you have a known, good algorithm that can
|
141
|
+
# use a recording mock object to record the steps. Then you compare
|
142
|
+
# the execution of a new algorithm to behavior of the old using the
|
143
|
+
# recorded expectations in the mock. For this you probably want to
|
144
|
+
# put the recorder in _strict_ mode so that the recorded
|
145
|
+
# expectations use exact matching on argument lists, and strict
|
146
|
+
# ordering of the method calls.
|
147
|
+
|
148
|
+
# <b>Note:</b> This is most useful when there are no queries on the
|
149
|
+
# mock objects, because the query responses cannot be programmed
|
150
|
+
# into the recorder object.
|
151
|
+
|
152
|
+
it "compares a know algorithm with a new algorithm" do
|
153
|
+
builder = flexmock('builder')
|
154
|
+
builder.should_expect do |rec|
|
155
|
+
rec.should_be_strict
|
156
|
+
known_good_way_to_build_xml(rec) # record the messages
|
157
|
+
end
|
158
|
+
new_way_to_build_xml(builder) # compare to new way
|
159
|
+
end
|
160
|
+
|
161
|
+
def known_good_way_to_build_xml(builder)
|
162
|
+
builder.person
|
163
|
+
end
|
164
|
+
|
165
|
+
def new_way_to_build_xml(builder)
|
166
|
+
builder.person
|
167
|
+
end
|
168
|
+
|
169
|
+
end
|
170
|
+
|
171
|
+
describe "Multiple Return Values" do
|
172
|
+
|
173
|
+
# Expect multiple calls, returning a different value each time
|
174
|
+
|
175
|
+
# Sometimes you need to return different values for each call to a
|
176
|
+
# mocked method. This example shifts values out of a list for this
|
177
|
+
# effect.
|
178
|
+
|
179
|
+
it "returns multiple values" do
|
180
|
+
file = flexmock('file')
|
181
|
+
file.should_receive(:gets).with_no_args.
|
182
|
+
and_return("line 1\n", "line 2\n")
|
183
|
+
|
184
|
+
# test code here
|
185
|
+
|
186
|
+
file.gets # returns "line 1"
|
187
|
+
file.gets # returns "line 2"
|
188
|
+
end
|
189
|
+
end
|
190
|
+
|
191
|
+
describe "Ignore Unimportant Messages" do
|
192
|
+
|
193
|
+
# Ignore uninteresting messages
|
194
|
+
|
195
|
+
# Generally you need to mock only those methods that return an
|
196
|
+
# interesting value or wish to assert were sent in a particular
|
197
|
+
# manner. Use the <code>should_ignore_missing</code> method to turn
|
198
|
+
# on missing method ignoring.
|
199
|
+
|
200
|
+
it "ignores unimportant messages" do
|
201
|
+
m = flexmock('m')
|
202
|
+
m.should_receive(:an_important_message).and_return(1).once
|
203
|
+
m.should_ignore_missing
|
204
|
+
|
205
|
+
# Test Code
|
206
|
+
|
207
|
+
m.an_important_message
|
208
|
+
m.an_unimportant_message
|
209
|
+
end
|
210
|
+
|
211
|
+
# When <code>should_ignore_missing</code> is enabled, ignored
|
212
|
+
# missing methods will return an undefined object. Any operation on
|
213
|
+
# the undefined object will return the undefined object.
|
214
|
+
|
215
|
+
end
|
216
|
+
|
217
|
+
|
218
|
+
describe "Partial Mocks" do
|
219
|
+
|
220
|
+
# Mock just one method on an existing object
|
221
|
+
|
222
|
+
# The Portfolio class calculate the value of a set of stocks by
|
223
|
+
# talking to a quote service via a web service. Since we don't want
|
224
|
+
# to use a real web service in our unit tests, we will mock the
|
225
|
+
# quote service.
|
226
|
+
|
227
|
+
it "returns the portfolio value" do
|
228
|
+
flexmock(QuoteService).new_instances do |m|
|
229
|
+
m.should_receive(:quote).and_return(100)
|
230
|
+
end
|
231
|
+
port = Portfolio.new
|
232
|
+
value = port.value # Portfolio calls QuoteService.quote
|
233
|
+
value.should == 100
|
234
|
+
end
|
235
|
+
|
236
|
+
class QuoteService
|
237
|
+
end
|
238
|
+
|
239
|
+
class Portfolio
|
240
|
+
def value
|
241
|
+
qs = QuoteService.new
|
242
|
+
qs.quote
|
243
|
+
end
|
244
|
+
end
|
245
|
+
end
|
@@ -0,0 +1,241 @@
|
|
1
|
+
= FlexMock Examples
|
2
|
+
require 'flexmock/test_unit'
|
3
|
+
|
4
|
+
class TestSimple < Test::Unit::TestCase
|
5
|
+
|
6
|
+
# Simple stubbing of some methods
|
7
|
+
|
8
|
+
def test_simple_mock
|
9
|
+
m = flexmock(:pi => 3.1416, :e => 2.71)
|
10
|
+
assert_equal 3.1416, m.pi
|
11
|
+
assert_equal 2.71, m.e
|
12
|
+
end
|
13
|
+
end
|
14
|
+
|
15
|
+
|
16
|
+
class TestUndefined < Test::Unit::TestCase
|
17
|
+
|
18
|
+
# Create a mock object that returns an undefined object for method calls
|
19
|
+
|
20
|
+
def test_undefined_values
|
21
|
+
m = flexmock("mock")
|
22
|
+
m.should_receive(:divide_by).with(0).
|
23
|
+
and_return_undefined
|
24
|
+
assert_equal FlexMock.undefined, m.divide_by(0)
|
25
|
+
end
|
26
|
+
end
|
27
|
+
|
28
|
+
class TestDb < Test::Unit::TestCase
|
29
|
+
|
30
|
+
# Expect multiple queries and a single update
|
31
|
+
|
32
|
+
# Multiple calls to the query method will be allows, and calls may
|
33
|
+
# have any argument list. Each call to query will return the three
|
34
|
+
# element array [1, 2, 3]. The call to update must have a specific
|
35
|
+
# argument of 5.
|
36
|
+
|
37
|
+
def test_db
|
38
|
+
db = flexmock('db')
|
39
|
+
db.should_receive(:query).and_return([1,2,3])
|
40
|
+
db.should_receive(:update).with(5).and_return(nil).once
|
41
|
+
|
42
|
+
# Test Code
|
43
|
+
|
44
|
+
db.query
|
45
|
+
db.update(5)
|
46
|
+
end
|
47
|
+
end
|
48
|
+
|
49
|
+
class TestOrdered < Test::Unit::TestCase
|
50
|
+
|
51
|
+
# Expect all queries before any updates
|
52
|
+
|
53
|
+
# All the query message must occur before any of the update
|
54
|
+
# messages.
|
55
|
+
|
56
|
+
def test_query_and_update
|
57
|
+
db = flexmock('db')
|
58
|
+
db.should_receive(:query).and_return([1,2,3]).ordered
|
59
|
+
db.should_receive(:update).and_return(nil).ordered
|
60
|
+
|
61
|
+
# test code here
|
62
|
+
|
63
|
+
db.query
|
64
|
+
db.update
|
65
|
+
end
|
66
|
+
end
|
67
|
+
|
68
|
+
class MoreOrdered < Test::Unit::TestCase
|
69
|
+
|
70
|
+
# Expect several queries with different parameters
|
71
|
+
|
72
|
+
# The queries should happen after startup but before finish. The
|
73
|
+
# queries themselves may happen in any order (because they are in
|
74
|
+
# the same order group). The first two queries should happen exactly
|
75
|
+
# once, but the third query (which matches any query call with a
|
76
|
+
# four character parameter) may be called multiple times (but at
|
77
|
+
# least once). Startup and finish must also happen exactly once.
|
78
|
+
|
79
|
+
# Also note that we use the <code>with</code> method to match
|
80
|
+
# different argument values to figure out what value to return.
|
81
|
+
|
82
|
+
def test_ordered_queries
|
83
|
+
db = flexmock('db')
|
84
|
+
db.should_receive(:startup).once.ordered
|
85
|
+
db.should_receive(:query).with("CPWR").and_return(12.3).
|
86
|
+
once.ordered(:queries)
|
87
|
+
db.should_receive(:query).with("MSFT").and_return(10.0).
|
88
|
+
once.ordered(:queries)
|
89
|
+
db.should_receive(:query).with(/^....$/).and_return(3.3).
|
90
|
+
at_least.once.ordered(:queries)
|
91
|
+
db.should_receive(:finish).once.ordered
|
92
|
+
|
93
|
+
# Test Code
|
94
|
+
|
95
|
+
db.startup
|
96
|
+
db.query("MSFT")
|
97
|
+
db.query("XYZY")
|
98
|
+
db.query("CPWR")
|
99
|
+
db.finish
|
100
|
+
end
|
101
|
+
end
|
102
|
+
|
103
|
+
class EvenMoreOrderedTest < Test::Unit::TestCase
|
104
|
+
|
105
|
+
# Same as above, but using the Record Mode interface
|
106
|
+
|
107
|
+
# The record mode interface offers much the same features as the
|
108
|
+
# <code>should_receive</code> interface introduced so far, but it
|
109
|
+
# allows the messages to be sent directly to a recording object
|
110
|
+
# rather than be specified indirectly using a symbol.
|
111
|
+
|
112
|
+
|
113
|
+
def test_ordered_queries_in_record_mode
|
114
|
+
db = flexmock('db')
|
115
|
+
db.should_expect do |rec|
|
116
|
+
rec.startup.once.ordered
|
117
|
+
rec.query("CPWR") { 12.3 }.once.ordered(:queries)
|
118
|
+
rec.query("MSFT") { 10.0 }.once.ordered(:queries)
|
119
|
+
rec.query(/^....$/) { 3.3 }.at_least.once.ordered(:queries)
|
120
|
+
rec.finish.once.ordered
|
121
|
+
end
|
122
|
+
|
123
|
+
# Test Code
|
124
|
+
|
125
|
+
db.startup
|
126
|
+
db.query("MSFT")
|
127
|
+
db.query("XYZY")
|
128
|
+
db.query("CPWR")
|
129
|
+
db.finish
|
130
|
+
end
|
131
|
+
end
|
132
|
+
|
133
|
+
class RecordedTest < Test::Unit::TestCase
|
134
|
+
|
135
|
+
# Using Record Mode to record a known, good algorithm for testing
|
136
|
+
|
137
|
+
# Record mode is nice when you have a known, good algorithm that can
|
138
|
+
# use a recording mock object to record the steps. Then you compare
|
139
|
+
# the execution of a new algorithm to behavior of the old using the
|
140
|
+
# recorded expectations in the mock. For this you probably want to
|
141
|
+
# put the recorder in _strict_ mode so that the recorded
|
142
|
+
# expectations use exact matching on argument lists, and strict
|
143
|
+
# ordering of the method calls.
|
144
|
+
|
145
|
+
# <b>Note:</b> This is most useful when there are no queries on the
|
146
|
+
# mock objects, because the query responses cannot be programmed
|
147
|
+
# into the recorder object.
|
148
|
+
|
149
|
+
def test_build_xml
|
150
|
+
builder = flexmock('builder')
|
151
|
+
builder.should_expect do |rec|
|
152
|
+
rec.should_be_strict
|
153
|
+
known_good_way_to_build_xml(rec) # record the messages
|
154
|
+
end
|
155
|
+
new_way_to_build_xml(builder) # compare to new way
|
156
|
+
end
|
157
|
+
|
158
|
+
def known_good_way_to_build_xml(builder)
|
159
|
+
builder.person
|
160
|
+
end
|
161
|
+
|
162
|
+
def new_way_to_build_xml(builder)
|
163
|
+
builder.person
|
164
|
+
end
|
165
|
+
|
166
|
+
end
|
167
|
+
|
168
|
+
class MultipleReturnValueTest < Test::Unit::TestCase
|
169
|
+
|
170
|
+
# Expect multiple calls, returning a different value each time
|
171
|
+
|
172
|
+
# Sometimes you need to return different values for each call to a
|
173
|
+
# mocked method. This example shifts values out of a list for this
|
174
|
+
# effect.
|
175
|
+
|
176
|
+
def test_multiple_gets
|
177
|
+
file = flexmock('file')
|
178
|
+
file.should_receive(:gets).with_no_args.
|
179
|
+
and_return("line 1\n", "line 2\n")
|
180
|
+
|
181
|
+
# test code here
|
182
|
+
|
183
|
+
file.gets # returns "line 1"
|
184
|
+
file.gets # returns "line 2"
|
185
|
+
end
|
186
|
+
end
|
187
|
+
|
188
|
+
class IgnoreUnimportantMessages < Test::Unit::TestCase
|
189
|
+
|
190
|
+
# Ignore uninteresting messages
|
191
|
+
|
192
|
+
# Generally you need to mock only those methods that return an
|
193
|
+
# interesting value or wish to assert were sent in a particular
|
194
|
+
# manner. Use the <code>should_ignore_missing</code> method to turn
|
195
|
+
# on missing method ignoring.
|
196
|
+
|
197
|
+
def test_an_important_message
|
198
|
+
m = flexmock('m')
|
199
|
+
m.should_receive(:an_important_message).and_return(1).once
|
200
|
+
m.should_ignore_missing
|
201
|
+
|
202
|
+
# Test Code
|
203
|
+
|
204
|
+
m.an_important_message
|
205
|
+
m.an_unimportant_message
|
206
|
+
end
|
207
|
+
|
208
|
+
# When <code>should_ignore_missing</code> is enabled, ignored
|
209
|
+
# missing methods will return an undefined object. Any operation on
|
210
|
+
# the undefined object will return the undefined object.
|
211
|
+
|
212
|
+
end
|
213
|
+
|
214
|
+
class PartialMockTest < Test::Unit::TestCase
|
215
|
+
|
216
|
+
# Mock just one method on an existing object
|
217
|
+
|
218
|
+
# The Portfolio class calculate the value of a set of stocks by
|
219
|
+
# talking to a quote service via a web service. Since we don't want
|
220
|
+
# to use a real web service in our unit tests, we will mock the
|
221
|
+
# quote service.
|
222
|
+
|
223
|
+
def test_portfolio_value
|
224
|
+
flexmock(QuoteService).new_instances do |m|
|
225
|
+
m.should_receive(:quote).and_return(100)
|
226
|
+
end
|
227
|
+
port = Portfolio.new
|
228
|
+
value = port.value # Portfolio calls QuoteService.quote
|
229
|
+
assert_equal 100, value
|
230
|
+
end
|
231
|
+
|
232
|
+
class QuoteService
|
233
|
+
end
|
234
|
+
|
235
|
+
class Portfolio
|
236
|
+
def value
|
237
|
+
qs = QuoteService.new
|
238
|
+
qs.quote
|
239
|
+
end
|
240
|
+
end
|
241
|
+
end
|
@@ -14,7 +14,7 @@ class FlexMock
|
|
14
14
|
def describe(spy, sym, args, options, not_clause="")
|
15
15
|
result = "expected "
|
16
16
|
result << call_description(sym, args)
|
17
|
-
result << " to#{not_clause} be
|
17
|
+
result << " to#{not_clause} be received by "
|
18
18
|
result << spy.inspect
|
19
19
|
result << times_description(options[:times])
|
20
20
|
result << block_description(options[:with_block])
|
data/lib/flexmock/version.rb
CHANGED
@@ -34,7 +34,7 @@ class AssertSpyCalledTest < Test::Unit::TestCase
|
|
34
34
|
|
35
35
|
def test_assert_rejects_incorrect_args
|
36
36
|
spy.foo(1,2)
|
37
|
-
messages = assert_fails(/^expected foo\(1, 3\) to be
|
37
|
+
messages = assert_fails(/^expected foo\(1, 3\) to be received by <FlexMock:AssertSpyCalledTest::FooBar Mock>/i) do
|
38
38
|
assert_spy_called spy, :foo, 1, 3
|
39
39
|
end
|
40
40
|
end
|
@@ -49,7 +49,7 @@ class AssertSpyCalledTest < Test::Unit::TestCase
|
|
49
49
|
def test_assert_rejects_incorrect_type
|
50
50
|
spy.foo
|
51
51
|
spy.foo
|
52
|
-
assert_fails(/^expected foo\(\) to be
|
52
|
+
assert_fails(/^expected foo\(\) to be received by <FlexMock:AssertSpyCalledTest::FooBar Mock> 3 times/i) do
|
53
53
|
assert_spy_called spy, {times: 3}, :foo
|
54
54
|
end
|
55
55
|
end
|
@@ -71,7 +71,7 @@ class AssertSpyCalledTest < Test::Unit::TestCase
|
|
71
71
|
|
72
72
|
def test_assert_rejects_bad_count_on_any_args
|
73
73
|
spy.foo
|
74
|
-
assert_fails(/^expected foo\(\.\.\.\) to be
|
74
|
+
assert_fails(/^expected foo\(\.\.\.\) to be received by <FlexMock:AssertSpyCalledTest::FooBar Mock> twice/i) do
|
75
75
|
assert_spy_called spy, {times: 2}, :foo, :_
|
76
76
|
end
|
77
77
|
end
|
@@ -31,13 +31,13 @@ describe "Dog" do
|
|
31
31
|
end
|
32
32
|
|
33
33
|
it "rejects wag(:foot)" do
|
34
|
-
should_fail(/^expected wag\(:foot\) to be
|
34
|
+
should_fail(/^expected wag\(:foot\) to be received by <FlexMock:Dog Mock>/i) do
|
35
35
|
dog.should have_received(:wag).with(:foot)
|
36
36
|
end
|
37
37
|
end
|
38
38
|
|
39
39
|
it "rejects not wag(:tail)" do
|
40
|
-
should_fail(/^expected wag\(:foot\) to be
|
40
|
+
should_fail(/^expected wag\(:foot\) to be received by <FlexMock:Dog Mock>/i) do
|
41
41
|
dog.should_not have_received(:wag).with(:tail)
|
42
42
|
end
|
43
43
|
end
|
@@ -58,7 +58,7 @@ describe "Dog" do
|
|
58
58
|
end
|
59
59
|
|
60
60
|
it "rejects wags(:tail) wrong times value" do
|
61
|
-
should_fail(/^expected wags\(:tail\) to be
|
61
|
+
should_fail(/^expected wags\(:tail\) to be received by <FlexMock:Dog Mock>/i) do
|
62
62
|
dog.should have_received(:wags).with(:tail).times(2)
|
63
63
|
end
|
64
64
|
end
|
@@ -72,7 +72,7 @@ describe "Dog" do
|
|
72
72
|
end
|
73
73
|
|
74
74
|
it "rejects an incorrect once" do
|
75
|
-
should_fail(/^expected wags\(:tail\) to be
|
75
|
+
should_fail(/^expected wags\(:tail\) to be received by <FlexMock:Dog Mock> once/i) do
|
76
76
|
dog.should have_received(:wags).with(:tail).once
|
77
77
|
end
|
78
78
|
end
|
@@ -82,7 +82,7 @@ describe "Dog" do
|
|
82
82
|
end
|
83
83
|
|
84
84
|
it "rejects an incorrect twice" do
|
85
|
-
should_fail(/^expected wags\(:tail\) to be
|
85
|
+
should_fail(/^expected wags\(:tail\) to be received by <FlexMock:Dog Mock> twice/) do
|
86
86
|
dog.should have_received(:wags).with(:tail).twice
|
87
87
|
end
|
88
88
|
end
|
@@ -92,7 +92,7 @@ describe "Dog" do
|
|
92
92
|
end
|
93
93
|
|
94
94
|
it "rejects an incorrect never" do
|
95
|
-
should_fail(/^expected barks\(\) to be
|
95
|
+
should_fail(/^expected barks\(\) to be received by <FlexMock:Dog Mock> never/i) do
|
96
96
|
dog.should have_received(:barks).with().never
|
97
97
|
end
|
98
98
|
end
|
data/test/spys_test.rb
CHANGED
@@ -7,6 +7,7 @@ class TestSpys < Test::Unit::TestCase
|
|
7
7
|
|
8
8
|
class FooBar
|
9
9
|
def foo
|
10
|
+
:foofoo
|
10
11
|
end
|
11
12
|
def bar
|
12
13
|
end
|
@@ -96,6 +97,20 @@ class TestSpys < Test::Unit::TestCase
|
|
96
97
|
assert_spy_called @spy, :foo
|
97
98
|
end
|
98
99
|
|
100
|
+
def test_spy_cannot_see_normal_methods
|
101
|
+
foo = FooBar.new
|
102
|
+
flexmock(foo)
|
103
|
+
assert_equal :foofoo, foo.foo
|
104
|
+
assert_spy_not_called foo, :foo
|
105
|
+
end
|
106
|
+
|
107
|
+
def test_spy_cannot_see_normal_methods2
|
108
|
+
foo = FooBar.new
|
109
|
+
flexmock(foo).should_receive(:foo).pass_thru
|
110
|
+
assert_equal :foofoo, foo.foo
|
111
|
+
assert_spy_called foo, :foo
|
112
|
+
end
|
113
|
+
|
99
114
|
def test_calling_non_spy_base_methods_is_an_error
|
100
115
|
assert_raise(NoMethodError) do
|
101
116
|
@spy.baz
|
metadata
CHANGED
@@ -1,8 +1,8 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: flexmock
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
prerelease:
|
5
|
-
version: 1.0.0
|
4
|
+
prerelease:
|
5
|
+
version: 1.0.0
|
6
6
|
platform: ruby
|
7
7
|
authors:
|
8
8
|
- Jim Weirich
|
@@ -10,7 +10,7 @@ autorequire:
|
|
10
10
|
bindir: bin
|
11
11
|
cert_chain: []
|
12
12
|
|
13
|
-
date: 2012-08-
|
13
|
+
date: 2012-08-23 00:00:00 Z
|
14
14
|
dependencies: []
|
15
15
|
|
16
16
|
description: "\n FlexMock is a extremely simple mock object class compatible\n with the Test::Unit framework. Although the FlexMock's\n interface is simple, it is very flexible.\n "
|
@@ -22,6 +22,8 @@ extensions: []
|
|
22
22
|
extra_rdoc_files:
|
23
23
|
- README.rdoc
|
24
24
|
- CHANGES
|
25
|
+
- doc/examples/rspec_examples_spec.rdoc
|
26
|
+
- doc/examples/test_unit_examples_test.rdoc
|
25
27
|
- doc/GoogleExample.rdoc
|
26
28
|
- doc/releases/flexmock-0.4.0.rdoc
|
27
29
|
- doc/releases/flexmock-0.4.1.rdoc
|
@@ -106,6 +108,8 @@ files:
|
|
106
108
|
- test/undefined_test.rb
|
107
109
|
- flexmock.blurb
|
108
110
|
- install.rb
|
111
|
+
- doc/examples/rspec_examples_spec.rdoc
|
112
|
+
- doc/examples/test_unit_examples_test.rdoc
|
109
113
|
- doc/GoogleExample.rdoc
|
110
114
|
- doc/releases/flexmock-0.4.0.rdoc
|
111
115
|
- doc/releases/flexmock-0.4.1.rdoc
|
@@ -148,9 +152,9 @@ required_ruby_version: !ruby/object:Gem::Requirement
|
|
148
152
|
required_rubygems_version: !ruby/object:Gem::Requirement
|
149
153
|
none: false
|
150
154
|
requirements:
|
151
|
-
- - "
|
155
|
+
- - ">="
|
152
156
|
- !ruby/object:Gem::Version
|
153
|
-
version:
|
157
|
+
version: "0"
|
154
158
|
requirements: []
|
155
159
|
|
156
160
|
rubyforge_project:
|