mocha 0.2.1 → 0.3.0

Sign up to get free protection for your applications and to get access to all the features.
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