mocha 0.3.1 → 0.3.2
Sign up to get free protection for your applications and to get access to all the features.
- data/README +17 -146
- data/Rakefile +60 -6
- data/lib/mocha.rb +1 -1
- data/lib/mocha/auto_verify.rb +101 -34
- data/lib/mocha/expectation.rb +112 -3
- data/lib/mocha/mock.rb +9 -2
- data/lib/mocha/mock_methods.rb +21 -1
- data/lib/smart_test_case.rb +1 -1
- data/lib/smart_test_case/multiple_setup_and_teardown.rb +91 -89
- data/lib/stubba.rb +1 -1
- data/lib/stubba/object.rb +33 -8
- data/lib/stubba/setup_and_teardown.rb +19 -17
- data/test/all_tests.rb +0 -7
- data/test/mocha/auto_verify_test.rb +16 -1
- data/test/mocha/expectation_test.rb +8 -6
- data/test/mocha/mock_methods_test.rb +1 -1
- data/test/mocha/mock_test.rb +10 -0
- data/test/mocha_test_result_integration_test.rb +2 -2
- data/test/smart_test_case/multiple_setup_and_teardown_test.rb +11 -11
- data/test/stubba/setup_and_teardown_test.rb +3 -3
- data/test/stubba_integration_test.rb +7 -7
- data/test/stubba_test_result_integration_test.rb +2 -2
- metadata +3 -12
- data/TODO +0 -14
- data/lib/auto_mocha.rb +0 -2
- data/lib/auto_mocha/auto_mock.rb +0 -54
- data/lib/auto_mocha/mock_class.rb +0 -38
- data/test/auto_mocha/auto_mock_test.rb +0 -85
- data/test/auto_mocha/mock_class_test.rb +0 -179
- data/test/auto_mock_acceptance_test.rb +0 -35
data/README
CHANGED
@@ -1,164 +1,35 @@
|
|
1
1
|
= Mocha
|
2
2
|
|
3
|
-
Mocha is a library for mocking and stubbing within
|
3
|
+
Mocha is a library for mocking and stubbing within a TestCase[http://www.ruby-doc.org/core/classes/Test/Unit.html] using a syntax like that of JMock[http://www.jmock.org], and SchMock[http://rubyforge.org/projects/schmock].
|
4
4
|
|
5
|
-
|
5
|
+
One of its main advantages is that it allows you to mock and stub methods on _real_ (non-mock) classes and instances. You can for example stub ActiveRecord[http://api.rubyonrails.com/classes/ActiveRecord/Base.html] instance methods like +create+, +save+, +destroy+ and even class methods like +find+ to avoid hitting the database in unit tests. This is a feature that is not currently offered by other Ruby[http://www.ruby-doc.org/] mocking libraries like FlexMock[http://onestepback.org/software/flexmock] and RSpec[http://rspec.rubyforge.org].
|
6
6
|
|
7
|
-
|
8
|
-
2. Stubba - allows mocking and stubbing of methods on real (non-mock) classes
|
9
|
-
3. AutoMocha - magically provides mocks in the place of undefined classes
|
10
|
-
4. SmartTestCase - allows addition of multiple setup and teardown methods for a Test::Unit::TestCase
|
7
|
+
Mocha provides a unified, simple and readable syntax for both traditional mocking and for mocking with non-mock objects.
|
11
8
|
|
12
|
-
|
13
|
-
|
14
|
-
== Provenance
|
15
|
-
|
16
|
-
Mocha and Stubba have been created by amalgamating a number of techniques developed by me (James[http:blog.floehopper.org]) and my Reevoo[http://www.reevoo.com] colleagues (Ben[http://www.reevoo.com/blogs/bengriffiths/], Chris[http://blog.seagul.co.uk] and Paul[http://po-ru.com]) into a common syntax. They are both in use on real-world Rails[http://www.rubyonrails.org] projects. AutoMocha is more experimental and is at an earlier stage of development. SmartTestCase has been exrtacted from Mocha and Stubba to remove duplication and allow its use in isolation.
|
9
|
+
Mocha has been harvested from projects at Reevoo[http://www.reevoo.com] by me (James[http://blog.floehopper.org]) and my colleagues Ben[http://www.reevoo.com/blogs/bengriffiths/], Chris[http://blog.seagul.co.uk] and Paul[http://po-ru.com]. Mocha is in use on real-world Rails[http://www.rubyonrails.org] projects.
|
17
10
|
|
18
11
|
== Download and Installation
|
19
12
|
|
20
|
-
|
13
|
+
Install the gem with the following command...
|
21
14
|
|
22
|
-
|
15
|
+
$ gem install mocha
|
23
16
|
|
24
|
-
|
17
|
+
Or install the Rails[http://www.rubyonrails.org] plugin...
|
25
18
|
|
26
19
|
$ script/plugin install svn://rubyforge.org/var/svn/mocha/trunk
|
27
20
|
|
28
|
-
|
29
|
-
|
30
|
-
Copyright Revieworld Ltd. 2006
|
31
|
-
|
32
|
-
You may use, copy and redistribute this library under the same terms as Ruby itself (see http://www.ruby-lang.org/en/LICENSE.txt) or under the MIT license (see http://mocha.rubyforge.org/files/MIT-LICENSE.html).
|
21
|
+
Or download Mocha from here - http://rubyforge.org/projects/mocha
|
33
22
|
|
34
23
|
== Examples
|
35
24
|
|
36
|
-
|
37
|
-
|
38
|
-
|
39
|
-
|
40
|
-
|
41
|
-
|
42
|
-
def initialize(dilithium)
|
43
|
-
@dilithium = dilithium
|
44
|
-
end
|
45
|
-
|
46
|
-
def go(warp_factor)
|
47
|
-
warp_factor.times { @dilithium.nuke(:anti_matter) }
|
48
|
-
end
|
49
|
-
|
50
|
-
end
|
51
|
-
|
52
|
-
require 'test/unit'
|
53
|
-
require 'rubygems'
|
54
|
-
require 'mocha'
|
55
|
-
|
56
|
-
class EnterpriseTest < Test::Unit::TestCase
|
57
|
-
|
58
|
-
def test_should_boldly_go
|
59
|
-
dilithium = mock()
|
60
|
-
dilithium.expects(:nuke).with(:anti_matter).at_least_once # auto-verified at end of test
|
61
|
-
enterprise = Enterprise.new(dilithium)
|
62
|
-
enterprise.go(2)
|
63
|
-
end
|
64
|
-
|
65
|
-
end
|
66
|
-
|
67
|
-
=== Stubba Example
|
68
|
-
|
69
|
-
class Order
|
70
|
-
|
71
|
-
attr_accessor :shipped_on
|
72
|
-
|
73
|
-
def total_cost
|
74
|
-
line_items.inject(0) { |total, line_item| total + line_item.price } + shipping_cost
|
75
|
-
end
|
76
|
-
|
77
|
-
def total_weight
|
78
|
-
line_items.inject(0) { |total, line_item| total + line_item.weight }
|
79
|
-
end
|
80
|
-
|
81
|
-
def shipping_cost
|
82
|
-
total_weight * 5 + 10
|
83
|
-
end
|
84
|
-
|
85
|
-
class << self
|
25
|
+
* Quick Start - {Usage Examples}[link:examples/misc.html]
|
26
|
+
* Traditional mocking - {Star Trek Example}[link:examples/mocha.html]
|
27
|
+
* Setting expectations on real classes - {Order Example}[link:examples/stubba.html]
|
28
|
+
* More examples on {Floehopper's Blog}[http://blog.floehopper.org]
|
29
|
+
* {Mailing List Archives}[http://rubyforge.org/pipermail/mocha-developer/]
|
86
30
|
|
87
|
-
|
88
|
-
# Database.connection.execute('select * from orders...
|
89
|
-
end
|
90
|
-
|
91
|
-
def number_shipped_since(date)
|
92
|
-
find_all.select { |order| order.shipped_on > date }.size
|
93
|
-
end
|
94
|
-
|
95
|
-
def unshipped_value
|
96
|
-
find_all.inject(0) { |total, order| order.shipped_on ? total : total + order.total_cost }
|
97
|
-
end
|
98
|
-
|
99
|
-
end
|
100
|
-
|
101
|
-
end
|
102
|
-
|
103
|
-
require 'test/unit'
|
104
|
-
require 'rubygems'
|
105
|
-
require 'stubba'
|
106
|
-
|
107
|
-
class OrderTest < Test::Unit::TestCase
|
108
|
-
|
109
|
-
# illustrates stubbing instance method
|
110
|
-
def test_should_calculate_shipping_cost_based_on_total_weight
|
111
|
-
order = Order.new
|
112
|
-
order.stubs(:total_weight).returns(10)
|
113
|
-
assert_equal 60, order.shipping_cost
|
114
|
-
end
|
115
|
-
|
116
|
-
# illustrates stubbing class method
|
117
|
-
def test_should_count_number_of_orders_shipped_after_specified_date
|
118
|
-
now = Time.now; week_in_secs = 7 * 24 * 60 * 60
|
119
|
-
order_1 = Order.new; order_1.shipped_on = now - 1 * week_in_secs
|
120
|
-
order_2 = Order.new; order_2.shipped_on = now - 3 * week_in_secs
|
121
|
-
Order.stubs(:find_all).returns([order_1, order_2])
|
122
|
-
assert_equal 1, Order.number_shipped_since(now - 2 * week_in_secs)
|
123
|
-
end
|
124
|
-
|
125
|
-
# illustrates stubbing instance method for all instances of a class
|
126
|
-
def test_should_calculate_value_of_unshipped_orders
|
127
|
-
Order.stubs(:find_all).returns([Order.new, Order.new, Order.new])
|
128
|
-
Order.any_instance.stubs(:shipped_on).returns(nil)
|
129
|
-
Order.any_instance.stubs(:total_cost).returns(10)
|
130
|
-
assert_equal 30, Order.unshipped_value
|
131
|
-
end
|
132
|
-
|
133
|
-
end
|
134
|
-
|
135
|
-
=== AutoMocha Example
|
136
|
-
|
137
|
-
class Article
|
138
|
-
|
139
|
-
attr_reader :id
|
140
|
-
|
141
|
-
def accepted_comments
|
142
|
-
Comment.find_all_by_article_id(self.id).select { |comment| comment.accepted? }
|
143
|
-
end
|
144
|
-
|
145
|
-
end
|
146
|
-
|
147
|
-
require 'rubygems'
|
148
|
-
require 'auto_mocha'
|
149
|
-
require 'test/unit'
|
150
|
-
|
151
|
-
class OrderTest < Test::Unit::TestCase
|
152
|
-
|
153
|
-
# illustrates stubbing of previously undefined class Comment
|
154
|
-
def test_should_return_accepted_comments_for_this_article
|
155
|
-
unaccepted_comment = stub(:accepted? => false)
|
156
|
-
accepted_comment = stub(:accepted? => true)
|
157
|
-
comments = [unaccepted_comment, accepted_comment]
|
158
|
-
Comment.stubs(:find_all_by_article_id).returns(comments)
|
159
|
-
article = Article.new
|
160
|
-
assert_equal [accepted_comment], article.accepted_comments
|
161
|
-
end
|
162
|
-
|
163
|
-
end
|
31
|
+
== License
|
164
32
|
|
33
|
+
Copyright Revieworld Ltd. 2006
|
34
|
+
|
35
|
+
You may use, copy and redistribute this library under the same terms as {Ruby itself}[http://www.ruby-lang.org/en/LICENSE.txt] or under the {MIT license}[http://mocha.rubyforge.org/files/MIT-LICENSE.html].
|
data/Rakefile
CHANGED
@@ -3,6 +3,10 @@ require 'rake/rdoctask'
|
|
3
3
|
require 'rake/gempackagetask'
|
4
4
|
require 'rake/contrib/sshpublisher'
|
5
5
|
|
6
|
+
module Mocha
|
7
|
+
VERSION = "0.3.2"
|
8
|
+
end
|
9
|
+
|
6
10
|
desc "Default task is currently to run all tests"
|
7
11
|
task :default => :test_all
|
8
12
|
|
@@ -17,12 +21,14 @@ Rake::RDocTask.new do |task|
|
|
17
21
|
task.main = 'README'
|
18
22
|
task.title = 'Mocha'
|
19
23
|
task.rdoc_dir = 'doc'
|
24
|
+
task.template = "html_with_google_analytics"
|
20
25
|
task.options << "--line-numbers" << "--inline-source"
|
21
|
-
task.rdoc_files.include('README', 'RELEASE', 'COPYING', 'MIT-LICENSE', 'agiledox.txt', 'lib
|
26
|
+
task.rdoc_files.include('README', 'RELEASE', 'COPYING', 'MIT-LICENSE', 'agiledox.txt', 'lib/mocha/auto_verify.rb', 'lib/mocha/mock_methods.rb', 'lib/mocha/expectation.rb', 'lib/stubba/object.rb')
|
22
27
|
end
|
28
|
+
task :rdoc => :examples
|
23
29
|
|
24
30
|
desc "Upload RDoc to RubyForge"
|
25
|
-
task :publish_rdoc => [:rdoc] do
|
31
|
+
task :publish_rdoc => [:rdoc, :examples] do
|
26
32
|
Rake::SshDirPublisher.new("jamesmead@rubyforge.org", "/var/www/gforge-projects/mocha", "doc").upload
|
27
33
|
end
|
28
34
|
|
@@ -42,16 +48,36 @@ file 'agiledox.txt' do
|
|
42
48
|
end
|
43
49
|
end
|
44
50
|
|
51
|
+
desc "Convert example ruby files to syntax-highlighted html"
|
52
|
+
task :examples do
|
53
|
+
require 'coderay'
|
54
|
+
mkdir_p 'doc/examples'
|
55
|
+
File.open('doc/examples/coderay.css', 'w') do |output|
|
56
|
+
output << CodeRay::Encoders[:html]::CSS.new.stylesheet
|
57
|
+
end
|
58
|
+
['mocha', 'stubba', 'misc'].each do |filename|
|
59
|
+
File.open("doc/examples/#{filename}.html", 'w') do |file|
|
60
|
+
file << "<html>"
|
61
|
+
file << "<head>"
|
62
|
+
file << %q(<link rel="stylesheet" media="screen" href="coderay.css" type="text/css">)
|
63
|
+
file << "</head>"
|
64
|
+
file << "<body>"
|
65
|
+
file << CodeRay.scan_file("examples/#{filename}.rb").html.div
|
66
|
+
file << "</body>"
|
67
|
+
file << "</html>"
|
68
|
+
end
|
69
|
+
end
|
70
|
+
end
|
71
|
+
|
45
72
|
Gem::manage_gems
|
46
73
|
|
47
74
|
specification = Gem::Specification.new do |s|
|
48
75
|
s.name = "mocha"
|
49
76
|
s.summary = "Mocking and stubbing library"
|
50
|
-
s.version =
|
77
|
+
s.version = Mocha::VERSION
|
51
78
|
s.author = 'James Mead'
|
52
79
|
s.description = <<-EOF
|
53
80
|
Mocking and stubbing library with JMock/SchMock syntax, which allows mocking and stubbing of methods on real (non-mock) classes.
|
54
|
-
Includes auto-mocking which magically provides mocks for undefined classes, facilitating unit tests with no external dependencies.
|
55
81
|
EOF
|
56
82
|
s.email = 'mocha-developer@rubyforge.org'
|
57
83
|
s.homepage = 'http://mocha.rubyforge.org'
|
@@ -62,11 +88,39 @@ specification = Gem::Specification.new do |s|
|
|
62
88
|
s.rdoc_options << '--title' << 'Mocha' << '--main' << 'README' << '--line-numbers'
|
63
89
|
|
64
90
|
s.autorequire = 'mocha'
|
65
|
-
s.files = FileList['{lib,test}/**/*.rb', '[A-Z]*'].to_a
|
91
|
+
s.files = FileList['{lib,test}/**/*.rb', '[A-Z]*'].exclude('TODO').to_a
|
66
92
|
s.test_file = "test/all_tests.rb"
|
67
93
|
end
|
68
94
|
|
69
95
|
Rake::GemPackageTask.new(specification) do |package|
|
70
96
|
package.need_zip = true
|
71
97
|
package.need_tar = true
|
72
|
-
end
|
98
|
+
end
|
99
|
+
|
100
|
+
task :verify_user do
|
101
|
+
raise "RUBYFORGE_USER environment variable not set!" unless ENV['RUBYFORGE_USER']
|
102
|
+
end
|
103
|
+
|
104
|
+
task :verify_password do
|
105
|
+
raise "RUBYFORGE_PASSWORD environment variable not set!" unless ENV['RUBYFORGE_PASSWORD']
|
106
|
+
end
|
107
|
+
|
108
|
+
desc "Publish package files on RubyForge."
|
109
|
+
task :publish_packages => [:verify_user, :verify_password, :package] do
|
110
|
+
require 'meta_project'
|
111
|
+
require 'rake/contrib/xforge'
|
112
|
+
release_files = FileList[
|
113
|
+
"pkg/mocha-#{Mocha::VERSION}.gem",
|
114
|
+
"pkg/mocha-#{Mocha::VERSION}.tgz",
|
115
|
+
"pkg/mocha-#{Mocha::VERSION}.zip"
|
116
|
+
]
|
117
|
+
|
118
|
+
Rake::XForge::Release.new(MetaProject::Project::XForge::RubyForge.new('mocha')) do |release|
|
119
|
+
release.user_name = ENV['RUBYFORGE_USER']
|
120
|
+
release.password = ENV['RUBYFORGE_PASSWORD']
|
121
|
+
release.files = release_files.to_a
|
122
|
+
release.release_name = "Mocha #{Mocha::VERSION}"
|
123
|
+
release.release_changes = ''
|
124
|
+
release.release_notes = ''
|
125
|
+
end
|
126
|
+
end
|
data/lib/mocha.rb
CHANGED
data/lib/mocha/auto_verify.rb
CHANGED
@@ -1,46 +1,113 @@
|
|
1
1
|
require 'mocha/mock'
|
2
2
|
|
3
|
-
|
3
|
+
# Methods added to TestCase allowing creation of mock objects.
|
4
|
+
#
|
5
|
+
# Mocks created this way will have their expectations automatically verified at the end of the test.
|
6
|
+
#
|
7
|
+
# See Mocha::MockMethods for methods on mock objects.
|
8
|
+
module Mocha
|
9
|
+
module AutoVerify
|
4
10
|
|
5
|
-
|
6
|
-
|
7
|
-
|
11
|
+
def self.included(base) # :nodoc:
|
12
|
+
base.add_teardown_method(:teardown_mocks)
|
13
|
+
end
|
8
14
|
|
9
|
-
|
10
|
-
|
11
|
-
|
15
|
+
def mocks # :nodoc:
|
16
|
+
@mocks ||= []
|
17
|
+
end
|
12
18
|
|
13
|
-
|
14
|
-
|
15
|
-
|
16
|
-
|
17
|
-
def mock(expectations = {})
|
18
|
-
build_mock_with_expectations(:expects, expectations)
|
19
|
-
end
|
19
|
+
def reset_mocks # :nodoc:
|
20
|
+
@mocks = nil
|
21
|
+
end
|
20
22
|
|
21
|
-
|
22
|
-
|
23
|
-
|
23
|
+
# :call-seq: mock(name) -> mock object
|
24
|
+
# mock(expected_methods = {}) -> mock object
|
25
|
+
# mock(name, expected_methods = {}) -> mock object
|
26
|
+
#
|
27
|
+
# Creates a mock object.
|
28
|
+
#
|
29
|
+
# +name+ is a +String+ identifier for the mock object.
|
30
|
+
#
|
31
|
+
# +expected_methods+ is a +Hash+ with expected method name symbols as keys and corresponding return values as values.
|
32
|
+
#
|
33
|
+
# Note that (contrary to expectations set up by #stub) these expectations <b>must</b> be fulfilled during the test.
|
34
|
+
# def test_product
|
35
|
+
# product = mock('ipod_product', :manufacturer => 'ipod', :price => 100)
|
36
|
+
# assert_equal 'ipod', product.manufacturer
|
37
|
+
# assert_equal 100, product.price
|
38
|
+
# # an error will be raised unless both Product#manufacturer and Product#price have been called
|
39
|
+
# end
|
40
|
+
def mock(*args)
|
41
|
+
name, expectations = name_and_expectations_from_args(args)
|
42
|
+
build_mock_with_expectations(:expects, expectations, name)
|
43
|
+
end
|
24
44
|
|
25
|
-
|
26
|
-
|
27
|
-
|
45
|
+
# :call-seq: stub(name) -> mock object
|
46
|
+
# stub(stubbed_methods = {}) -> mock object
|
47
|
+
# stub(name, stubbed_methods = {}) -> mock object
|
48
|
+
#
|
49
|
+
# Creates a mock object.
|
50
|
+
#
|
51
|
+
# +name+ is a +String+ identifier for the mock object.
|
52
|
+
#
|
53
|
+
# +stubbed_methods+ is a +Hash+ with stubbed method name symbols as keys and corresponding return values as values.
|
54
|
+
#
|
55
|
+
# Note that (contrary to expectations set up by #mock) these expectations <b>need not</b> be fulfilled during the test.
|
56
|
+
# def test_product
|
57
|
+
# product = stub('ipod_product', :manufacturer => 'ipod', :price => 100)
|
58
|
+
# assert_equal 'ipod', product.manufacturer
|
59
|
+
# assert_equal 100, product.price
|
60
|
+
# # an error will not be raised even if Product#manufacturer and Product#price have not been called
|
61
|
+
# end
|
62
|
+
def stub(*args)
|
63
|
+
name, expectations = name_and_expectations_from_args(args)
|
64
|
+
build_mock_with_expectations(:stubs, expectations, name)
|
65
|
+
end
|
66
|
+
|
67
|
+
# :call-seq: stub_everything(name) -> mock object
|
68
|
+
# stub_everything(stubbed_methods = {}) -> mock object
|
69
|
+
# stub_everything(name, stubbed_methods = {}) -> mock object
|
70
|
+
#
|
71
|
+
# Creates a mock object that accepts calls to any method.
|
72
|
+
#
|
73
|
+
# By default it will return +nil+ for any method call.
|
74
|
+
#
|
75
|
+
# +name+ and +stubbed_methods+ work in the same way as for #stub.
|
76
|
+
# def test_product
|
77
|
+
# product = stub_everything('ipod_product', :price => 100)
|
78
|
+
# assert_nil product.manufacturer
|
79
|
+
# assert_nil product.any_old_method
|
80
|
+
# assert_equal 100, product.price
|
81
|
+
# end
|
82
|
+
def stub_everything(*args)
|
83
|
+
name, expectations = name_and_expectations_from_args(args)
|
84
|
+
build_mock_with_expectations(:stub_everything, expectations, name)
|
85
|
+
end
|
28
86
|
|
29
|
-
|
30
|
-
|
31
|
-
|
32
|
-
|
87
|
+
def teardown_mocks # :nodoc:
|
88
|
+
mocks.each { |mock| mock.verify { add_assertion } }
|
89
|
+
reset_mocks
|
90
|
+
end
|
33
91
|
|
34
|
-
|
35
|
-
|
36
|
-
|
37
|
-
|
38
|
-
|
39
|
-
|
92
|
+
def build_mock_with_expectations(expectation_type = :expects, expectations = {}, name = nil) # :nodoc:
|
93
|
+
stub_everything = (expectation_type == :stub_everything)
|
94
|
+
expectation_type = :stubs if expectation_type == :stub_everything
|
95
|
+
mock = Mocha::Mock.new(stub_everything, name)
|
96
|
+
expectations.each do |method, result|
|
97
|
+
mock.send(expectation_type, method).returns(result)
|
98
|
+
end
|
99
|
+
mocks << mock
|
100
|
+
mock
|
101
|
+
end
|
102
|
+
|
103
|
+
private
|
104
|
+
|
105
|
+
def name_and_expectations_from_args(args) # :nodoc:
|
106
|
+
name = args.first.is_a?(String) ? args.delete_at(0) : nil
|
107
|
+
expectations = args.first || {}
|
108
|
+
[name, expectations]
|
40
109
|
end
|
41
|
-
|
42
|
-
mock
|
110
|
+
|
43
111
|
end
|
44
112
|
|
45
|
-
end
|
46
|
-
|
113
|
+
end
|
data/lib/mocha/expectation.rb
CHANGED
@@ -2,8 +2,11 @@ require 'mocha/infinite_range'
|
|
2
2
|
require 'mocha/pretty_parameters'
|
3
3
|
|
4
4
|
module Mocha
|
5
|
+
# Methods on expectations returned from Mocha::MockMethods#expects and Mocha::MockMethods#stubs
|
5
6
|
class Expectation
|
6
7
|
|
8
|
+
# :stopdoc:
|
9
|
+
|
7
10
|
class InvalidExpectation < Exception; end
|
8
11
|
|
9
12
|
class AlwaysEqual
|
@@ -37,47 +40,146 @@ module Mocha
|
|
37
40
|
end
|
38
41
|
end
|
39
42
|
|
43
|
+
# :startdoc:
|
44
|
+
|
45
|
+
# :call-seq: times(range) -> expectation
|
46
|
+
#
|
47
|
+
# Modifies expectation so that the number of calls to the expected method must be within a specific +range+.
|
48
|
+
#
|
49
|
+
# +range+ can be specified as an exact integer or as a range of integers
|
50
|
+
# object = mock()
|
51
|
+
# object.expects(:expected_method).times(3)
|
52
|
+
# 3.times { object.expected_method } # => verify succeeds
|
53
|
+
#
|
54
|
+
# object = mock()
|
55
|
+
# object.expects(:expected_method).times(3)
|
56
|
+
# 2.times { object.expected_method } # => verify fails
|
57
|
+
#
|
58
|
+
# object = mock()
|
59
|
+
# object.expects(:expected_method).times(2..4)
|
60
|
+
# 3.times { object.expected_method } # => verify succeeds
|
61
|
+
#
|
62
|
+
# object = mock()
|
63
|
+
# object.expects(:expected_method).times(2..4)
|
64
|
+
# object.expected_method # => verify fails
|
40
65
|
def times(range)
|
41
66
|
@count = range
|
42
67
|
self
|
43
68
|
end
|
44
69
|
|
70
|
+
# :call-seq: never -> expectation
|
71
|
+
#
|
72
|
+
# Modifies expectation so that the expected method must never be called.
|
73
|
+
# object = mock()
|
74
|
+
# object.expects(:expected_method).never
|
75
|
+
# object.expected_method # => verify fails
|
76
|
+
#
|
77
|
+
# object = mock()
|
78
|
+
# object.expects(:expected_method).never
|
79
|
+
# object.expected_method # => verify succeeds
|
45
80
|
def never
|
46
81
|
times(0)
|
47
82
|
end
|
48
83
|
|
84
|
+
# :call-seq: at_least(minimum) -> expectation
|
85
|
+
#
|
86
|
+
# Modifies expectation so that the expected method must be called at least a +minimum+ number of times.
|
87
|
+
# object = mock()
|
88
|
+
# object.expects(:expected_method).at_least(2)
|
89
|
+
# 3.times { object.expected_method } # => verify succeeds
|
90
|
+
#
|
91
|
+
# object = mock()
|
92
|
+
# object.expects(:expected_method).at_least(2)
|
93
|
+
# object.expected_method # => verify fails
|
49
94
|
def at_least(minimum)
|
50
95
|
times(Range.at_least(minimum))
|
51
96
|
self
|
52
97
|
end
|
53
98
|
|
99
|
+
# :call-seq: at_least_once() -> expectation
|
100
|
+
#
|
101
|
+
# Modifies expectation so that the expected method must be called at least once.
|
102
|
+
# object = mock()
|
103
|
+
# object.expects(:expected_method).at_least_once
|
104
|
+
# object.expected_method # => verify succeeds
|
105
|
+
#
|
106
|
+
# object = mock()
|
107
|
+
# object.expects(:expected_method).at_least_once
|
108
|
+
# # => verify fails
|
54
109
|
def at_least_once()
|
55
110
|
at_least(1)
|
56
111
|
self
|
57
112
|
end
|
58
113
|
|
114
|
+
# :call-seq: with(*arguments, ¶meter_block) -> expectation
|
115
|
+
#
|
116
|
+
# Modifies expectation so that the expected method must be called with specified +arguments+.
|
117
|
+
# object = mock()
|
118
|
+
# object.expects(:expected_method).with(:param1, :param2)
|
119
|
+
# object.expected_method(:param1, :param2) # => verify succeeds
|
120
|
+
#
|
121
|
+
# object = mock()
|
122
|
+
# object.expects(:expected_method).with(:param1, :param2)
|
123
|
+
# object.expected_method(:param3) # => verify fails
|
124
|
+
# If a +parameter_block+ is given, the block is called with the parameters passed to the expected method.
|
125
|
+
# The expectation is matched if the block evaluates to +true+.
|
126
|
+
# object = mock()
|
127
|
+
# object.expects(:expected_method).with() { |value| value % 4 == 0 }
|
128
|
+
# object.expected_method(16) # => verify succeeds
|
129
|
+
#
|
130
|
+
# object = mock()
|
131
|
+
# object.expects(:expected_method).with() { |value| value % 4 == 0 }
|
132
|
+
# object.expected_method(17) # => verify fails
|
59
133
|
def with(*arguments, ¶meter_block)
|
60
134
|
@parameters, @parameter_block = arguments, parameter_block
|
61
135
|
class << @parameters; def to_s; join(', '); end; end
|
62
136
|
self
|
63
137
|
end
|
64
138
|
|
139
|
+
# :call-seq: yields(*parameters) -> expectation
|
140
|
+
#
|
141
|
+
# Modifies expectation so that when the expected method is called, it yields with the specified +parameters+.
|
142
|
+
# object = mock()
|
143
|
+
# object.expects(:expected_method).yields('result')
|
144
|
+
# yielded_value = nil
|
145
|
+
# object.expected_method { |value| yielded_value = value }
|
146
|
+
# yielded_value # => 'result'
|
65
147
|
def yields(*parameters)
|
66
148
|
@yield = true
|
67
149
|
@parameters_to_yield = parameters
|
68
150
|
self
|
69
151
|
end
|
70
152
|
|
153
|
+
# :call-seq: returns(value) -> expectation
|
154
|
+
#
|
155
|
+
# Modifies expectation so that when the expected method is called, it returns the specified +value+.
|
156
|
+
# object = mock()
|
157
|
+
# object.expects(:expected_method).returns('result')
|
158
|
+
# object.expected_method # => 'result'
|
159
|
+
# If +value+ is a Proc, then expected method will return result of calling Proc.
|
160
|
+
# object = mock()
|
161
|
+
# results = [111, 222]
|
162
|
+
# object.stubs(:expected_method).returns(lambda { results.shift })
|
163
|
+
# object.expected_method # => 111
|
164
|
+
# object.expected_method # => 222
|
71
165
|
def returns(value)
|
72
166
|
@return_value = value
|
73
167
|
self
|
74
168
|
end
|
75
169
|
|
170
|
+
# :call-seq: raises(exception = RuntimeError, message = nil) -> expectation
|
171
|
+
#
|
172
|
+
# Modifies expectation so that when the expected method is called, it raises the specified +exception+ with the specified +message+.
|
173
|
+
# object = mock()
|
174
|
+
# object.expects(:expected_method).raises(Exception, 'message')
|
175
|
+
# object.expected_method # => raises exception of class Exception and with message 'message'
|
76
176
|
def raises(exception = RuntimeError, message = nil)
|
77
177
|
@return_value = lambda{ raise exception, message }
|
78
178
|
self
|
79
179
|
end
|
80
180
|
|
181
|
+
# :stopdoc:
|
182
|
+
|
81
183
|
def invoke
|
82
184
|
@invoked += 1
|
83
185
|
yield(*@parameters_to_yield) if yield? and block_given?
|
@@ -99,8 +201,12 @@ module Mocha
|
|
99
201
|
":#{@method_name}(#{params.pretty})"
|
100
202
|
end
|
101
203
|
|
204
|
+
# :startdoc:
|
205
|
+
|
102
206
|
end
|
103
207
|
|
208
|
+
# :stopdoc:
|
209
|
+
|
104
210
|
class Stub < Expectation
|
105
211
|
|
106
212
|
def verify
|
@@ -111,14 +217,14 @@ module Mocha
|
|
111
217
|
|
112
218
|
class MissingExpectation < Expectation
|
113
219
|
|
114
|
-
def initialize(method_name, expectations = [])
|
220
|
+
def initialize(method_name, mock, expectations = [])
|
115
221
|
super(method_name)
|
116
|
-
@expectations = expectations
|
222
|
+
@mock, @expectations = mock, expectations
|
117
223
|
@invoked = true
|
118
224
|
end
|
119
225
|
|
120
226
|
def verify
|
121
|
-
msg = "Unexpected message #{message}"
|
227
|
+
msg = "Unexpected message #{message} sent to #{@mock.mocha_inspect}"
|
122
228
|
msg << "\nSimilar expectations #{similar_expectations.collect { |expectation| expectation.message }.join("\n") }" unless similar_expectations.empty?
|
123
229
|
raise Test::Unit::AssertionFailedError, msg if @invoked
|
124
230
|
end
|
@@ -128,4 +234,7 @@ module Mocha
|
|
128
234
|
end
|
129
235
|
|
130
236
|
end
|
237
|
+
|
238
|
+
# :startdoc:
|
239
|
+
|
131
240
|
end
|