Skip to content

Commit

Permalink
Merge pull request #331 from freerange/avoid-mocks-for-partial-mockin…
Browse files Browse the repository at this point in the history
…g-leaking-into-subsequent-tests

Avoid mocks for partial mocking leaking into subsequent tests
  • Loading branch information
floehopper committed Apr 21, 2018
2 parents 82c9e56 + 953cebc commit 183fefc
Show file tree
Hide file tree
Showing 8 changed files with 152 additions and 11 deletions.
8 changes: 6 additions & 2 deletions lib/mocha/class_methods.rb
Expand Up @@ -19,8 +19,12 @@ def initialize(klass)
@stubba_object = klass
end

def mocha
@mocha ||= Mocha::Mockery.instance.mock_impersonating_any_instance_of(@stubba_object)
def mocha(instantiate = true)
if instantiate
@mocha ||= Mocha::Mockery.instance.mock_impersonating_any_instance_of(@stubba_object)
else
defined?(@mocha) ? @mocha : nil
end
end

def stubba_method
Expand Down
8 changes: 6 additions & 2 deletions lib/mocha/object_methods.rb
Expand Up @@ -14,8 +14,12 @@ module ObjectMethods
alias_method :_method, :method

# @private
def mocha
@mocha ||= Mocha::Mockery.instance.mock_impersonating(self)
def mocha(instantiate = true)
if instantiate
@mocha ||= Mocha::Mockery.instance.mock_impersonating(self)
else
defined?(@mocha) ? @mocha : nil
end
end

# @private
Expand Down
6 changes: 4 additions & 2 deletions lib/mocha/receivers.rb
Expand Up @@ -9,7 +9,8 @@ def initialize(object)
def mocks
object, mocks = @object, []
while object do
mocks << object.mocha
mocha = object.mocha(false)
mocks << mocha if mocha
object = object.is_a?(Class) ? object.superclass : nil
end
mocks
Expand All @@ -26,7 +27,8 @@ def initialize(klass)
def mocks
klass, mocks = @klass, []
while klass do
mocks << klass.any_instance.mocha
mocha = klass.any_instance.mocha(false)
mocks << mocha if mocha
klass = klass.superclass
end
mocks
Expand Down
Expand Up @@ -31,4 +31,34 @@ def my_superclass_method
end
assert_equal :original_return_value, instance.my_superclass_method
end
end

def test_expect_method_on_any_instance_of_superclass_even_if_preceded_by_test_expecting_method_on_any_instance_of_subclass
superklass = Class.new do
def self.inspect
'superklass'
end
def my_instance_method; end
end
klass = Class.new(superklass) do
def self.inspect
'klass'
end
def my_instance_method; end
end
test_result = run_as_tests(
:test_1 => lambda {
klass.any_instance.expects(:my_instance_method)
klass.new.my_instance_method
},
:test_2 => lambda {
superklass.any_instance.expects(:my_instance_method)
}
)
assert_failed(test_result)
assert_equal [
"not all expectations were satisfied",
"unsatisfied expectations:",
"- expected exactly once, not yet invoked: #<AnyInstance:superklass>.my_instance_method(any_parameters)"
], test_result.failure_message_lines
end
end
30 changes: 30 additions & 0 deletions test/acceptance/stub_class_method_defined_on_superclass_test.rb
Expand Up @@ -109,4 +109,34 @@ def my_class_method
end
assert_passed(test_result)
end

def test_expect_method_on_superclass_even_if_preceded_by_test_expecting_method_on_subclass
superklass = Class.new do
def self.inspect
'superklass'
end
def self.my_class_method; end
end
klass = Class.new(superklass) do
def self.inspect
'klass'
end
def self.my_class_method; end
end
test_result = run_as_tests(
:test_1 => lambda {
klass.expects(:my_class_method)
klass.my_class_method
},
:test_2 => lambda {
superklass.expects(:my_class_method)
}
)
assert_failed(test_result)
assert_equal [
"not all expectations were satisfied",
"unsatisfied expectations:",
"- expected exactly once, not yet invoked: superklass.my_class_method(any_parameters)"
], test_result.failure_message_lines
end
end
31 changes: 31 additions & 0 deletions test/unit/class_methods_test.rb
@@ -1,13 +1,20 @@
require File.expand_path('../../test_helper', __FILE__)
require 'mocha/class_methods'
require 'mocha/object_methods'
require 'mocha/mockery'
require 'mocha/names'

class ClassMethodsTest < Mocha::TestCase

def setup
Mocha::Mockery.setup
@klass = Class.new.extend(Mocha::ClassMethods, Mocha::ObjectMethods)
end

def teardown
Mocha::Mockery.teardown
end

def test_should_build_any_instance_object
any_instance = @klass.any_instance
assert_not_nil any_instance
Expand All @@ -20,6 +27,30 @@ def test_should_return_same_any_instance_object
assert_equal any_instance_1, any_instance_2
end

def test_any_instance_should_build_mocha_referring_to_klass
mocha = @klass.any_instance.mocha
assert_not_nil mocha
assert mocha.is_a?(Mocha::Mock)
expected_name = Mocha::ImpersonatingAnyInstanceName.new(@klass).mocha_inspect
assert_equal expected_name, mocha.mocha_inspect
end

def test_any_instance_should_not_build_mocha_if_instantiate_is_false
assert_nil @klass.any_instance.mocha(false)
end

def test_any_instance_should_reuse_existing_mocha
mocha_1 = @klass.any_instance.mocha
mocha_2 = @klass.any_instance.mocha
assert_equal mocha_1, mocha_2
end

def test_any_instance_should_reuse_existing_mocha_even_if_instantiate_is_false
mocha_1 = @klass.any_instance.mocha
mocha_2 = @klass.any_instance.mocha(false)
assert_equal mocha_1, mocha_2
end

def test_should_use_stubba_class_method_for_class
assert_equal Mocha::ClassMethod, @klass.stubba_method
end
Expand Down
14 changes: 13 additions & 1 deletion test/unit/object_methods_test.rb
Expand Up @@ -3,6 +3,7 @@
require 'mocha/mockery'
require 'mocha/mock'
require 'mocha/expectation_error_factory'
require 'mocha/names'

class ObjectMethodsTest < Mocha::TestCase

Expand All @@ -19,7 +20,12 @@ def test_should_build_mocha_referring_to_self
mocha = @object.mocha
assert_not_nil mocha
assert mocha.is_a?(Mocha::Mock)
assert_equal @object.mocha_inspect, mocha.mocha_inspect
expected_name = Mocha::ImpersonatingName.new(@object).mocha_inspect
assert_equal expected_name, mocha.mocha_inspect
end

def test_should_not_build_mocha_if_instantiate_is_false
assert_nil @object.mocha(false)
end

def test_should_reuse_existing_mocha
Expand All @@ -28,6 +34,12 @@ def test_should_reuse_existing_mocha
assert_equal mocha_1, mocha_2
end

def test_should_reuse_existing_mocha_even_if_instantiate_is_false
mocha_1 = @object.mocha
mocha_2 = @object.mocha(false)
assert_equal mocha_1, mocha_2
end

def test_should_reset_mocha
assert_nil @object.reset_mocha
end
Expand Down
34 changes: 31 additions & 3 deletions test/unit/receivers_test.rb
Expand Up @@ -4,13 +4,31 @@
class ObjectReceiverTest < Mocha::TestCase
include Mocha

class FakeObject < Struct.new(:mocha)
class FakeObject
def initialize(mocha)
@mocha = mocha
end

def mocha(_)
@mocha
end

def is_a?(klass)
false
end
end

class FakeClass < Struct.new(:superclass, :mocha)
class FakeClass
attr_reader :superclass

def initialize(superclass, mocha)
@superclass, @mocha = superclass, mocha
end

def mocha(_)
@mocha
end

def is_a?(klass)
klass == Class
end
Expand All @@ -35,14 +53,24 @@ class AnyInstanceReceiverTest < Mocha::TestCase
include Mocha

class FakeAnyInstanceClass
class AnyInstance
def initialize(mocha)
@mocha = mocha
end

def mocha(_)
@mocha
end
end

attr_reader :superclass

def initialize(superclass, mocha)
@superclass, @mocha = superclass, mocha
end

def any_instance
Struct.new(:mocha).new(@mocha)
AnyInstance.new(@mocha)
end
end

Expand Down

0 comments on commit 183fefc

Please sign in to comment.