mocha 0.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.
Files changed (43) hide show
  1. data/COPYING +3 -0
  2. data/README +148 -0
  3. data/lib/auto_mocha.rb +1 -0
  4. data/lib/auto_mocha/auto_mock.rb +54 -0
  5. data/lib/auto_mocha/mock_class.rb +38 -0
  6. data/lib/mocha.rb +1 -0
  7. data/lib/mocha/expectation.rb +116 -0
  8. data/lib/mocha/infinite_range.rb +27 -0
  9. data/lib/mocha/inspect.rb +37 -0
  10. data/lib/mocha/metaclass.rb +7 -0
  11. data/lib/mocha/mock.rb +30 -0
  12. data/lib/mocha/mock_methods.rb +55 -0
  13. data/lib/mocha/pretty_parameters.rb +28 -0
  14. data/lib/stubba.rb +2 -0
  15. data/lib/stubba/any_instance_method.rb +31 -0
  16. data/lib/stubba/class_method.rb +61 -0
  17. data/lib/stubba/instance_method.rb +22 -0
  18. data/lib/stubba/object.rb +77 -0
  19. data/lib/stubba/stubba.rb +27 -0
  20. data/lib/stubba/test_case.rb +65 -0
  21. data/test/all_tests.rb +100 -0
  22. data/test/auto_mocha/auto_mock_test.rb +85 -0
  23. data/test/auto_mocha/mock_class_test.rb +179 -0
  24. data/test/auto_mock_acceptance_test.rb +36 -0
  25. data/test/method_definer.rb +18 -0
  26. data/test/mocha/expectation_test.rb +216 -0
  27. data/test/mocha/infinite_range_test.rb +50 -0
  28. data/test/mocha/inspect_test.rb +79 -0
  29. data/test/mocha/mock_methods_test.rb +141 -0
  30. data/test/mocha/mock_test.rb +64 -0
  31. data/test/mocha/pretty_parameters_test.rb +32 -0
  32. data/test/mocha_acceptance_test.rb +112 -0
  33. data/test/stubba/any_instance_method_test.rb +113 -0
  34. data/test/stubba/class_method_test.rb +149 -0
  35. data/test/stubba/instance_method_test.rb +97 -0
  36. data/test/stubba/object_test.rb +147 -0
  37. data/test/stubba/stubba_test.rb +62 -0
  38. data/test/stubba/test_case_test.rb +41 -0
  39. data/test/stubba_acceptance_test.rb +107 -0
  40. data/test/stubba_integration_test.rb +59 -0
  41. data/test/stubba_replacer.rb +13 -0
  42. data/test/test_helper.rb +4 -0
  43. metadata +91 -0
data/COPYING ADDED
@@ -0,0 +1,3 @@
1
+ Copyright Revieworld Ltd. 2006
2
+
3
+ You may use, copy and redistribute this library under the same terms as Ruby itself (see http://www.ruby-lang.org/en/LICENSE.txt).
data/README ADDED
@@ -0,0 +1,148 @@
1
+ = Mocha
2
+
3
+ Mocha is a library for mocking and stubbing with unit tests using a syntax like that of JMock[http://www.jmock.org] and SchMock[http://rubyforge.org/projects/schmock].
4
+
5
+ Mocha comes in three parts:
6
+
7
+ 1. Mocha - traditional mock objects with expectations and verification
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
+
11
+ Stubba & AutoMocha are the main difference between this mocking library and others like FlexMock[http://onestepback.org/software/flexmock] and RSpec[http://rspec.rubyforge.org].
12
+
13
+ == Provenance
14
+
15
+ Mocha & Stubba have been created by amalgamating a number of techniques developed by my Reevoo[http://www.reevoo.com] colleagues (Ben[http://www.reevoo.com/blogs/bengriffiths/], Chris[http://blog.seagul.co.uk] & Paul[http://po-ru.com]) and I[http:blog.floehopper.org] under a common syntax. They are both in use on real-world projects using {Ruby On Rails}[http://www.rubyonrails.org]. AutoMocha is more experimental and is at an earlier stage of development.
16
+
17
+ == Download & Installation
18
+
19
+ You can download Mocha from here[http://rubyforge.org/projects/mocha] or install Mocha with the following command.
20
+
21
+ $ gem install flexmock
22
+
23
+ == License
24
+
25
+ Copyright Revieworld Ltd. 2006
26
+
27
+ You may use, copy and redistribute this library under the same terms as Ruby itself (see http://www.ruby-lang.org/en/LICENSE.txt).
28
+
29
+ == Simple Mocha Example
30
+
31
+ class Enterprise
32
+
33
+ def initialize(dilithium)
34
+ @dilithium = dilithium
35
+ end
36
+
37
+ def go(warp_factor)
38
+ warp_factor.times { @dilithium.nuke(:anti_matter) }
39
+ end
40
+
41
+ end
42
+
43
+ require 'mocha'
44
+
45
+ class EnterpriseTest < Test::Unit::TestCase
46
+
47
+ include Mocha
48
+
49
+ def test_should_boldly_go
50
+ dilithium = Mock.new
51
+ dilithium.expects(:nuke).with(:anti_matter).at_least_once
52
+ enterprise = Enterprise.new(dilithium)
53
+ enterprise.go(2)
54
+ dilithium.verify
55
+ end
56
+
57
+ end
58
+
59
+ == Simple Stubba Example
60
+
61
+ class Order
62
+
63
+ def total_cost
64
+ line_items.inject(0) { |total, line_item| total + line_item.price } + shipping_cost
65
+ end
66
+
67
+ def total_weight
68
+ line_items.inject(0) { |total, line_item| total + line_item.weight }
69
+ end
70
+
71
+ def shipping_cost
72
+ total_weight * 5 + 10
73
+ end
74
+
75
+ class << self
76
+
77
+ def find_all
78
+ Database.connection.select_all('select * from orders')
79
+ end
80
+
81
+ def number_shipped_since(date)
82
+ find_all.select { |order| order.shipped_on > date }.size
83
+ end
84
+
85
+ def unshipped_value
86
+ find_all.inject(0) { |order| order.shipped_on ? 0 : order.total_cost }
87
+ end
88
+
89
+ end
90
+ end
91
+
92
+ require 'stubba'
93
+
94
+ class OrderTest < Test::Unit::TestCase
95
+
96
+ # illustrates stubbing instance method
97
+ def test_should_calculate_shipping_cost_based_on_total_weight
98
+ order = Order.new
99
+ order.stubs(:total_weight).returns(10)
100
+ assert_equal 60, order.shipping_cost
101
+ end
102
+
103
+ # illustrates stubbing class method
104
+ def test_should_count_number_of_orders_shipped_after_specified_date
105
+ order_1 = Order.new(:shipped_on => 1.week.ago)
106
+ order_2 = Order.new(:shipped_on => 3.weeks.ago)
107
+ Order.stubs(:find_all).returns([order_1, order_2])
108
+ assert_equal 1, Order.number_shipped_since(2.weeks.ago)
109
+ end
110
+
111
+ # illustrates stubbing instance method for all instances of a class
112
+ def test_should_calculate_value_of_unshipped_orders
113
+ order_1 = Order.create
114
+ order_2 = Order.create
115
+ Order.any_instance.stubs(:shipped_on).returns(Time.now)
116
+ Order.any_instance.stubs(:total_cost).returns(10)
117
+ assert_equal 20, Order.unshipped_value
118
+ end
119
+
120
+ end
121
+
122
+ == Simple AutoMocha Example
123
+
124
+ class Article
125
+
126
+ def accepted_comments
127
+ Comment.find_all_by_article_id(self.id).select { |comment| comment.accepted? }
128
+ end
129
+
130
+ end
131
+
132
+ require 'auto_mocha'
133
+
134
+ class OrderTest < Test::Unit::TestCase
135
+
136
+ include Mocha
137
+
138
+ # illustrates stubbing of previously undefined class Comment
139
+ def test_should_return_accepted_comments_for_this_article
140
+ unaccepted_comment = Mock.new(:accepted? => false)
141
+ accepted_comment = Mock.new(:accepted? => true)
142
+ comments = [unaccepted_comment, accepted_comment]
143
+ Comment.stubs(:find_all_by_article_id).returns(comments)
144
+ article = Article.new
145
+ assert_equal [accepted_comment], article.accepted_comments
146
+ end
147
+
148
+ end
@@ -0,0 +1 @@
1
+ require 'auto_mocha/auto_mock'
@@ -0,0 +1,54 @@
1
+ require 'auto_mocha/mock_class'
2
+
3
+ class Module
4
+
5
+ def mochas
6
+ @@mochas ||= {}
7
+ end
8
+
9
+ def reset_mochas
10
+ @@mochas = nil
11
+ end
12
+
13
+ def const_missing(symbol)
14
+ mochas[symbol] ||= Mocha::MockClass.dup
15
+ end
16
+
17
+ def verify_all
18
+ mochas.each_value { |mocha| mocha.verify_all }
19
+ end
20
+
21
+ end
22
+
23
+ Mocha::MockClass.class_eval do
24
+
25
+ class << self
26
+
27
+ def mochas
28
+ @mochas ||= {}
29
+ end
30
+
31
+ def const_missing(symbol)
32
+ mochas[symbol] ||= Mocha::MockClass.dup
33
+ end
34
+
35
+ def verify_all
36
+ mochas.each_value { |mocha| mocha.verify }
37
+ verify
38
+ end
39
+
40
+ end
41
+
42
+ end
43
+
44
+ class Test::Unit::TestCase
45
+
46
+ def reset_mochas
47
+ Object.reset_mochas
48
+ end
49
+
50
+ def verify_all
51
+ Object.verify_all
52
+ end
53
+
54
+ end
@@ -0,0 +1,38 @@
1
+ require 'mocha/mock_methods'
2
+
3
+ module Mocha
4
+
5
+ class MockClass
6
+
7
+ include MockMethods
8
+
9
+ class << self
10
+
11
+ include MockMethods
12
+
13
+ def super_method_missing(symbol, *arguments, &block)
14
+ superclass.method_missing(symbol, *arguments, &block)
15
+ end
16
+
17
+ alias_method :__new__, :new
18
+
19
+ def new(*arguments, &block)
20
+ method_missing(:new, *arguments, &block)
21
+ end
22
+
23
+ def inherited(subclass)
24
+ subclass.class_eval do
25
+
26
+ def self.new(*arguments, &block)
27
+ __new__(*arguments, &block)
28
+ end
29
+
30
+ end
31
+
32
+ end
33
+
34
+ end
35
+
36
+ end
37
+
38
+ end
@@ -0,0 +1 @@
1
+ require 'mocha/mock'
@@ -0,0 +1,116 @@
1
+ require 'mocha/infinite_range'
2
+ require 'mocha/pretty_parameters'
3
+
4
+ module Mocha
5
+ class Expectation
6
+
7
+ class InvalidExpectation < Exception; end
8
+
9
+ class AlwaysEqual
10
+ def ==(other)
11
+ true
12
+ end
13
+ def to_s
14
+ "** any **"
15
+ end
16
+ end
17
+
18
+ attr_reader :method_name
19
+
20
+ def initialize(method_name)
21
+ @method_name = method_name
22
+ @count = 1
23
+ @parameters, @parameter_block = AlwaysEqual.new, nil
24
+ @invoked, @return_value = 0, nil
25
+ end
26
+
27
+ def match?(method_name, *arguments)
28
+ if @parameter_block then
29
+ @parameter_block.call(*arguments)
30
+ else
31
+ (@method_name == method_name) and (@parameters == arguments)
32
+ end
33
+ end
34
+
35
+ def times(range)
36
+ @count = range
37
+ self
38
+ end
39
+
40
+ def never
41
+ times(0)
42
+ end
43
+
44
+ def at_least(minimum)
45
+ times(Range.at_least(minimum))
46
+ self
47
+ end
48
+
49
+ def at_least_once()
50
+ at_least(1)
51
+ self
52
+ end
53
+
54
+ def with(*arguments, &parameter_block)
55
+ @parameters, @parameter_block = arguments, parameter_block
56
+ class << @parameters; def to_s; join(', '); end; end
57
+ self
58
+ end
59
+
60
+ def returns(value)
61
+ @return_value = value
62
+ self
63
+ end
64
+
65
+ def raises(exception = RuntimeError, message = nil)
66
+ @return_value = lambda{ raise exception, message }
67
+ self
68
+ end
69
+
70
+ def invoke
71
+ @invoked += 1
72
+ @return_value.is_a?(Proc) ? @return_value.call : @return_value
73
+ end
74
+
75
+ def verify
76
+ unless (@count === @invoked) then
77
+ raise Test::Unit::AssertionFailedError, "#{message}: expected calls: #{@count}, actual calls: #{@invoked}"
78
+ end
79
+ end
80
+
81
+ def message
82
+ params = @parameters.is_a?(Array) ? @parameters : [@parameters.to_s]
83
+ params = PrettyParameters.new(params)
84
+ ":#{@method_name}(#{params.pretty})"
85
+ end
86
+
87
+ end
88
+
89
+ class Stub < Expectation
90
+
91
+ def verify
92
+ true
93
+ end
94
+
95
+ end
96
+
97
+ class MissingExpectation < Expectation
98
+
99
+ def initialize(method_name, expectations = [])
100
+ super(method_name)
101
+ @expectations = expectations
102
+ @invoked = true
103
+ end
104
+
105
+ def verify
106
+ msg = "Unexpected message #{message}"
107
+ msg << "\nSimilar expectations #{similar_expectations.collect { |expectation| expectation.message }.join("\n") }" unless similar_expectations.empty?
108
+ raise Test::Unit::AssertionFailedError, msg if @invoked
109
+ end
110
+
111
+ def similar_expectations
112
+ @expectations.select { |expectation| expectation.method_name == self.method_name }
113
+ end
114
+
115
+ end
116
+ end
@@ -0,0 +1,27 @@
1
+ class Range
2
+
3
+ def self.at_least(minimum_value)
4
+ Range.new(minimum_value, infinite)
5
+ end
6
+
7
+ def self.at_most(maximum_value)
8
+ Range.new(-infinite, maximum_value, false)
9
+ end
10
+
11
+ def self.infinite
12
+ 1/0.0
13
+ end
14
+
15
+ alias_method :__to_s__, :to_s
16
+
17
+ def to_s
18
+ if first.to_f.infinite? then
19
+ return "at most #{last}"
20
+ elsif last.to_f.infinite? then
21
+ return "at least #{first}"
22
+ else
23
+ __to_s__
24
+ end
25
+ end
26
+
27
+ end
@@ -0,0 +1,37 @@
1
+ require 'date'
2
+
3
+ class Object
4
+ def mocha_inspect
5
+ inspect =~ /#</ ? "#<#{self.class}: #{self.object_id}>" : inspect
6
+ end
7
+ end
8
+
9
+ class String
10
+ def mocha_inspect
11
+ inspect.gsub(/\"/, "'")
12
+ end
13
+ end
14
+
15
+ class Array
16
+ def mocha_inspect
17
+ "[#{collect { |member| member.mocha_inspect }.join(', ')}]"
18
+ end
19
+ end
20
+
21
+ class Hash
22
+ def mocha_inspect
23
+ "{#{collect { |key, value| "#{key.mocha_inspect} => #{value.mocha_inspect}" }.join(', ')}}"
24
+ end
25
+ end
26
+
27
+ class Time
28
+ def mocha_inspect
29
+ "#{inspect} (#{to_f} secs)"
30
+ end
31
+ end
32
+
33
+ class Date
34
+ def mocha_inspect
35
+ to_s
36
+ end
37
+ end
@@ -0,0 +1,7 @@
1
+ class Object
2
+
3
+ def metaclass
4
+ class << self; self; end
5
+ end
6
+
7
+ end
@@ -0,0 +1,30 @@
1
+ require 'mocha/mock_methods'
2
+ require 'test/unit/assertions'
3
+
4
+ module Mocha
5
+ class Mock
6
+
7
+ include MockMethods
8
+
9
+ attr_reader :mocked
10
+
11
+ def initialize(*arguments)
12
+ @mocked = arguments.shift unless arguments.first.is_a?(Hash)
13
+ @mocked ||= always_responds
14
+ expectations = arguments.shift || {}
15
+ expectations.each do |method_name, result|
16
+ expects(method_name).returns(result)
17
+ end
18
+ end
19
+
20
+ def expects(symbol)
21
+ raise Test::Unit::AssertionFailedError, "Cannot replace #{symbol} as #{@mocked} does not respond to it." unless @mocked.respond_to?(symbol)
22
+ super
23
+ end
24
+
25
+ def always_responds
26
+ Class.new { def respond_to?(symbol); true; end }.new
27
+ end
28
+
29
+ end
30
+ end