mocha 0.2.1 → 0.3.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 CHANGED
@@ -20,6 +20,10 @@ Mocha and Stubba have been created by amalgamating a number of techniques develo
20
20
  You can download Mocha from here[http://rubyforge.org/projects/mocha] or install it with the following command.
21
21
 
22
22
  $ gem install mocha
23
+
24
+ == Ruby on Rails plugin
25
+
26
+ $ script/plugin install svn://rubyforge.org/var/svn/mocha/trunk
23
27
 
24
28
  == License
25
29
 
@@ -45,9 +49,9 @@ See MochaAcceptanceTest, StubbaAcceptanceTest, AutoMochaAcceptanceTest and unit
45
49
 
46
50
  end
47
51
 
52
+ require 'test/unit'
48
53
  require 'rubygems'
49
54
  require 'mocha'
50
- require 'test/unit'
51
55
 
52
56
  class EnterpriseTest < Test::Unit::TestCase
53
57
 
@@ -96,9 +100,9 @@ See MochaAcceptanceTest, StubbaAcceptanceTest, AutoMochaAcceptanceTest and unit
96
100
 
97
101
  end
98
102
 
103
+ require 'test/unit'
99
104
  require 'rubygems'
100
105
  require 'stubba'
101
- require 'test/unit'
102
106
 
103
107
  class OrderTest < Test::Unit::TestCase
104
108
 
data/RELEASE CHANGED
@@ -1,3 +1,14 @@
1
+ = 0.3.0
2
+
3
+ * Rails plugin.
4
+ * Auto-verify for expectations on concrete classes.
5
+ * Include each expectation verification in the test result assertion count.
6
+ * Filter out noise from assertion backtraces.
7
+ * Point assertion backtrace to line where failing expectation was created.
8
+ * New yields method for expectations.
9
+ * Create stubs which stub all method calls.
10
+ * Mocks now respond_to? expected methods.
11
+
1
12
  = 0.2.1
2
13
 
3
14
  * Rename MochaAcceptanceTest::Rover#move method to avoid conflict with Rake (in Ruby 1.8.4 only?)
data/Rakefile CHANGED
@@ -47,7 +47,7 @@ Gem::manage_gems
47
47
  specification = Gem::Specification.new do |s|
48
48
  s.name = "mocha"
49
49
  s.summary = "Mocking and stubbing library"
50
- s.version = "0.2.1"
50
+ s.version = "0.3.0"
51
51
  s.author = 'James Mead'
52
52
  s.description = <<-EOF
53
53
  Mocking and stubbing library with JMock/SchMock syntax, which allows mocking and stubbing of methods on real (non-mock) classes.
data/TODO CHANGED
@@ -1,15 +1,13 @@
1
+ - think about behaviour when more than one expectation/stubbed method match
1
2
  - make verify method private (makes this unnecessary - fail if attempt to verify stub)
2
- - auto-verify for stubs?
3
3
  - allow hash parameter for stubs and expects methods particularly for stubba
4
4
  - write rdoc for most important methods/classes e.g. expectation
5
- - remove mocha/stubba from call stack when assertion error raised
6
5
  - test for setting expectations on class methods (and instance methods?) from within TestCase#setup
7
6
  - use Object#inspect(:mocha) instead of Object#mocha_inspect?
8
7
  - allow stubbing of private/protected methods?
9
8
  - should all instances share expectations for any_instance or should each instance have their own - in which case how do we provide access to the instances
10
9
  - detect existing or added definition of mocha methods e.g. expects and alias to __expects?
11
10
  - allow switch off of auto-verify?
12
- - fail if verify called with no expectations? set expectation - expects(:blah).never to ensure method not called
13
11
  - maybe use blank_slate as mocha parent class to allow mocking of standard object methods?
14
12
  - more jmock style stuff - e.g. return values on consecutive calls, labels/required order, more sophisticated param matching?
15
13
  - stubs should only return a fixed value - no blocks allowed for return values and no parameter expectations allowed?
@@ -1,5 +1,6 @@
1
1
  require 'smart_test_case'
2
2
  require 'mocha/auto_verify'
3
+ require 'shared/backtracefilter'
3
4
 
4
5
  class Test::Unit::TestCase
5
6
  include AutoVerify unless ancestors.include?(AutoVerify)
@@ -15,27 +15,32 @@ module AutoVerify
15
15
  end
16
16
 
17
17
  def mock(expectations = {})
18
- mock = Mocha::Mock.new
19
- expectations.each do |method, result|
20
- mock.expects(method).returns(result)
21
- end
22
- mocks << mock
23
- mock
18
+ build_mock_with_expectations(:expects, expectations)
24
19
  end
25
20
 
26
21
  def stub(expectations = {})
27
- mock = Mocha::Mock.new
28
- expectations.each do |method, result|
29
- mock.stubs(method).returns(result)
30
- end
31
- mocks << mock
32
- mock
22
+ build_mock_with_expectations(:stubs, expectations)
23
+ end
24
+
25
+ def stub_everything(expectations = {})
26
+ build_mock_with_expectations(:stub_everything, expectations)
33
27
  end
34
28
 
35
29
  def teardown_mocks
36
- mocks.each { |mock| mock.verify }
30
+ mocks.each { |mock| mock.verify { add_assertion } }
37
31
  reset_mocks
38
32
  end
39
33
 
34
+ def build_mock_with_expectations(expectation_type = :expects, expectations = {})
35
+ stub_everything = (expectation_type == :stub_everything)
36
+ expectation_type = :stubs if expectation_type == :stub_everything
37
+ mock = Mocha::Mock.new(stub_everything)
38
+ expectations.each do |method, result|
39
+ mock.send(expectation_type, method).returns(result)
40
+ end
41
+ mocks << mock
42
+ mock
43
+ end
44
+
40
45
  end
41
46
 
@@ -15,13 +15,18 @@ module Mocha
15
15
  end
16
16
  end
17
17
 
18
- attr_reader :method_name
18
+ attr_reader :method_name, :backtrace
19
19
 
20
- def initialize(method_name)
20
+ def initialize(method_name, backtrace = nil)
21
21
  @method_name = method_name
22
22
  @count = 1
23
23
  @parameters, @parameter_block = AlwaysEqual.new, nil
24
24
  @invoked, @return_value = 0, nil
25
+ @backtrace = backtrace || caller
26
+ end
27
+
28
+ def yield?
29
+ @yield
25
30
  end
26
31
 
27
32
  def match?(method_name, *arguments)
@@ -57,6 +62,12 @@ module Mocha
57
62
  self
58
63
  end
59
64
 
65
+ def yields(*parameters)
66
+ @yield = true
67
+ @parameters_to_yield = parameters
68
+ self
69
+ end
70
+
60
71
  def returns(value)
61
72
  @return_value = value
62
73
  self
@@ -69,12 +80,16 @@ module Mocha
69
80
 
70
81
  def invoke
71
82
  @invoked += 1
83
+ yield(*@parameters_to_yield) if yield? and block_given?
72
84
  @return_value.is_a?(Proc) ? @return_value.call : @return_value
73
85
  end
74
86
 
75
87
  def verify
88
+ yield(self) if block_given?
76
89
  unless (@count === @invoked) then
77
- raise Test::Unit::AssertionFailedError, "#{message}: expected calls: #{@count}, actual calls: #{@invoked}"
90
+ failure = Test::Unit::AssertionFailedError.new("#{message}: expected calls: #{@count}, actual calls: #{@invoked}")
91
+ failure.set_backtrace(backtrace)
92
+ raise failure
78
93
  end
79
94
  end
80
95
 
@@ -1,30 +1,13 @@
1
1
  require 'mocha/mock_methods'
2
- require 'test/unit/assertions'
3
2
 
4
3
  module Mocha
5
4
  class Mock
6
5
 
7
6
  include MockMethods
8
7
 
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
8
+ def initialize(stub_everything = false)
9
+ @stub_everything = stub_everything
23
10
  end
24
11
 
25
- def always_responds
26
- Class.new { def respond_to?(symbol); true; end }.new
27
- end
28
-
29
12
  end
30
13
  end
@@ -2,25 +2,29 @@ require 'mocha/expectation'
2
2
 
3
3
  module Mocha
4
4
  module MockMethods
5
+
6
+ attr_reader :stub_everything
5
7
 
6
8
  def expectations
7
9
  @expectations ||= []
8
10
  end
9
11
 
10
- def expects(symbol)
11
- expectations << Expectation.new(symbol)
12
+ def expects(symbol, backtrace = nil)
13
+ expectations << Expectation.new(symbol, backtrace)
12
14
  expectations.last
13
15
  end
14
16
 
15
- def stubs(symbol)
16
- expectations << Stub.new(symbol)
17
+ def stubs(symbol, backtrace = nil)
18
+ expectations << Stub.new(symbol, backtrace)
17
19
  expectations.last
18
20
  end
19
-
21
+
20
22
  def method_missing(symbol, *arguments, &block)
21
23
  matching_expectation = matching_expectation(symbol, *arguments)
22
24
  if matching_expectation then
23
- matching_expectation.invoke
25
+ matching_expectation.invoke(&block)
26
+ elsif stub_everything then
27
+ return
24
28
  else
25
29
  begin
26
30
  super_method_missing(symbol, *arguments, &block)
@@ -29,6 +33,10 @@ module Mocha
29
33
  end
30
34
  end
31
35
  end
36
+
37
+ def respond_to?(symbol)
38
+ expectations.any? { |expectation| expectation.method_name == symbol }
39
+ end
32
40
 
33
41
  def super_method_missing(symbol, *arguments, &block)
34
42
  raise NoMethodError
@@ -42,13 +50,8 @@ module Mocha
42
50
  expectations.detect { |expectation| expectation.match?(symbol, *arguments) }
43
51
  end
44
52
 
45
- def expectations_matching(method_names)
46
- expectations.select { |expectation| method_names.include?(expectation.method_name) }
47
- end
48
-
49
- def verify(*method_names)
50
- for_verification = method_names.empty? ? expectations : expectations_matching(method_names)
51
- for_verification.each { |expectation| expectation.verify }
53
+ def verify(&block)
54
+ expectations.each { |expectation| expectation.verify(&block) }
52
55
  end
53
56
 
54
57
  end
@@ -0,0 +1,46 @@
1
+ module Test
2
+ module Unit
3
+ module Util
4
+ module BacktraceFilter
5
+
6
+ # quick rip-off of Test::Unit code - there must be a better way of doing this!
7
+
8
+ MOCHA_FILE_SEPARATORS = %r{[\\/:]}
9
+ MOCHA_PREFIX = __FILE__.split(MOCHA_FILE_SEPARATORS)[0..-3]
10
+ MOCHA_RB_FILE = /\.rb\Z/
11
+
12
+ alias_method :__filter_backtrace__, :filter_backtrace
13
+
14
+ def filter_backtrace(backtrace, prefix=nil)
15
+ backtrace = __filter_backtrace__(backtrace, prefix)
16
+ return ["No backtrace"] unless(backtrace)
17
+ split_p = if(prefix)
18
+ prefix.split(MOCHA_FILE_SEPARATORS)
19
+ else
20
+ MOCHA_PREFIX
21
+ end
22
+ match = proc do |e|
23
+ split_e = e.split(MOCHA_FILE_SEPARATORS)[0, split_p.size]
24
+ next false unless(split_e[0..-2] == split_p[0..-2])
25
+ split_e[-1].sub(MOCHA_RB_FILE, '') == split_p[-1]
26
+ end
27
+ return backtrace unless(backtrace.detect(&match))
28
+ found_prefix = false
29
+ new_backtrace = backtrace.reverse.reject do |e|
30
+ if(match[e])
31
+ found_prefix = true
32
+ true
33
+ elsif(found_prefix)
34
+ false
35
+ else
36
+ true
37
+ end
38
+ end.reverse
39
+ new_backtrace = (new_backtrace.empty? ? backtrace : new_backtrace)
40
+ new_backtrace = new_backtrace.reject(&match)
41
+ new_backtrace.empty? ? backtrace : new_backtrace
42
+ end
43
+ end
44
+ end
45
+ end
46
+ end
@@ -105,7 +105,15 @@ module MultipleSetupAndTeardown
105
105
  end
106
106
 
107
107
  def teardown_mocha
108
- self.class.class_eval { teardown_methods }.reverse.each { |symbol| send(symbol) }
108
+ stored_exception = nil
109
+ self.class.class_eval { teardown_methods }.reverse.each do |symbol|
110
+ begin
111
+ send(symbol)
112
+ rescue Exception => e
113
+ stored_exception ||= e
114
+ end
115
+ end
116
+ raise stored_exception if stored_exception
109
117
  end
110
118
 
111
119
  end
@@ -1,6 +1,7 @@
1
1
  require 'stubba/object'
2
2
  require 'smart_test_case'
3
3
  require 'stubba/setup_and_teardown'
4
+ require 'shared/backtracefilter'
4
5
 
5
6
  class Test::Unit::TestCase
6
7
  include SetupAndTeardown unless ancestors.include?(SetupAndTeardown)
@@ -7,23 +7,27 @@ module Stubba
7
7
  def unstub
8
8
  remove_new_method
9
9
  restore_original_method
10
- object.any_instance.reset_mocha
10
+ stubbee.any_instance.reset_mocha
11
+ end
12
+
13
+ def mock
14
+ stubbee.any_instance.mocha
11
15
  end
12
16
 
13
17
  def hide_original_method
14
- object.class_eval "alias_method :#{hidden_method}, :#{method}" if object.method_defined?(method)
18
+ stubbee.class_eval "alias_method :#{hidden_method}, :#{method}" if stubbee.method_defined?(method)
15
19
  end
16
20
 
17
21
  def define_new_method
18
- object.class_eval "def #{method}(*args, &block); self.class.any_instance.mocha.method_missing(:#{method}, *args, &block); end"
22
+ stubbee.class_eval "def #{method}(*args, &block); self.class.any_instance.mocha.method_missing(:#{method}, *args, &block); end"
19
23
  end
20
24
 
21
25
  def remove_new_method
22
- object.class_eval "remove_method :#{method}"
26
+ stubbee.class_eval "remove_method :#{method}"
23
27
  end
24
28
 
25
29
  def restore_original_method
26
- object.class_eval "alias_method :#{method}, :#{hidden_method}; remove_method :#{hidden_method}" if object.method_defined?(hidden_method)
30
+ stubbee.class_eval "alias_method :#{method}, :#{hidden_method}; remove_method :#{hidden_method}" if stubbee.method_defined?(hidden_method)
27
31
  end
28
32
 
29
33
  end
@@ -14,6 +14,14 @@ module Stubba
14
14
  stubba_methods.push method
15
15
  end
16
16
  end
17
+
18
+ def verify_all(&block)
19
+ unique_mocks.each { |mock| mock.verify(&block) }
20
+ end
21
+
22
+ def unique_mocks
23
+ stubba_methods.collect { |method| method.mock }.uniq
24
+ end
17
25
 
18
26
  def unstub_all
19
27
  while stubba_methods.size > 0
@@ -4,10 +4,10 @@ module Stubba
4
4
 
5
5
  class ClassMethod
6
6
 
7
- attr_reader :object, :method
7
+ attr_reader :stubbee, :method
8
8
 
9
- def initialize(object, method)
10
- @object, @method = object, method
9
+ def initialize(stubbee, method)
10
+ @stubbee, @method = stubbee, method
11
11
  end
12
12
 
13
13
  def stub
@@ -18,23 +18,27 @@ module Stubba
18
18
  def unstub
19
19
  remove_new_method
20
20
  restore_original_method
21
- object.reset_mocha
21
+ stubbee.reset_mocha
22
+ end
23
+
24
+ def mock
25
+ stubbee.mocha
22
26
  end
23
27
 
24
28
  def hide_original_method
25
- object.metaclass.class_eval "alias_method :#{hidden_method}, :#{method}" if object.metaclass.method_defined?(method)
29
+ stubbee.metaclass.class_eval "alias_method :#{hidden_method}, :#{method}" if stubbee.metaclass.method_defined?(method)
26
30
  end
27
31
 
28
32
  def define_new_method
29
- object.metaclass.class_eval "def #{method}(*args, &block); mocha.method_missing(:#{method}, *args, &block); end"
33
+ stubbee.metaclass.class_eval "def #{method}(*args, &block); mocha.method_missing(:#{method}, *args, &block); end"
30
34
  end
31
35
 
32
36
  def remove_new_method
33
- object.metaclass.class_eval "remove_method :#{method}"
37
+ stubbee.metaclass.class_eval "remove_method :#{method}"
34
38
  end
35
39
 
36
40
  def restore_original_method
37
- object.metaclass.class_eval "alias_method :#{method}, :#{hidden_method}; remove_method :#{hidden_method}" if object.metaclass.method_defined?(hidden_method)
41
+ stubbee.metaclass.class_eval "alias_method :#{method}, :#{hidden_method}; remove_method :#{hidden_method}" if stubbee.metaclass.method_defined?(hidden_method)
38
42
  end
39
43
 
40
44
  def hidden_method
@@ -42,18 +46,18 @@ module Stubba
42
46
  end
43
47
 
44
48
  def cannot_replace_method_error
45
- Test::Unit::AssertionFailedError.new("Cannot replace #{method} because it is not defined in #{object}.")
49
+ Test::Unit::AssertionFailedError.new("Cannot replace #{method} because it is not defined in #{stubbee}.")
46
50
  end
47
51
 
48
52
  def eql?(other)
49
53
  return false unless (other.class == self.class)
50
- (object == other.object) and (method == other.method)
54
+ (stubbee == other.stubbee) and (method == other.method)
51
55
  end
52
56
 
53
57
  alias_method :==, :eql?
54
58
 
55
59
  def to_s
56
- "#{object}.#{method}"
60
+ "#{stubbee}.#{method}"
57
61
  end
58
62
 
59
63
  end