Skip to content

Commit

Permalink
Include the original test name in expired stub error messages
Browse files Browse the repository at this point in the history
It is currently very hard to track down such leaked stubs, when
they happen, as they might be created in one test and cause an
exception dozens of tests later.

By including the name of the test that invalidated them, it makes
the debugging process a bit easier.

Note, I made `setup` also accept the current test instance purely
for consistency with `teardown` but it's not strictly necessary.
  • Loading branch information
byroot committed Apr 8, 2024
1 parent ce31b54 commit 0fc6ed1
Show file tree
Hide file tree
Showing 8 changed files with 28 additions and 23 deletions.
4 changes: 2 additions & 2 deletions lib/mocha/hooks.rb
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,7 @@ module Hooks
#
# This method should be called before each individual test starts (including before any "setup" code).
def mocha_setup
Mockery.setup
Mockery.setup(self)
end

# Verifies that all mock expectations have been met (only for use by authors of test libraries).
Expand All @@ -36,7 +36,7 @@ def mocha_verify(assertion_counter = nil)
#
# This method should be called after each individual test has finished (including after any "teardown" code).
def mocha_teardown
Mockery.teardown
Mockery.teardown(self)
end
end
end
6 changes: 3 additions & 3 deletions lib/mocha/mock.rb
Original file line number Diff line number Diff line change
Expand Up @@ -343,8 +343,8 @@ def __verified__?(assertion_counter = nil)
end

# @private
def __expire__
@expired = true
def __expire__(test_name)
@expired = test_name
end

# @private
Expand Down Expand Up @@ -394,7 +394,7 @@ def check_expiry
return unless @expired

sentences = [
"#{mocha_inspect} was instantiated in one test but it is receiving invocations within another test.",
"#{mocha_inspect} was instantiated in #{@expired} but it is receiving invocations within another test.",
'This can lead to unintended interactions between tests and hence unexpected test failures.',
'Ensure that every test correctly cleans up any state that it introduces.'
]
Expand Down
11 changes: 6 additions & 5 deletions lib/mocha/mockery.rb
Original file line number Diff line number Diff line change
Expand Up @@ -37,7 +37,7 @@ def instance
@instances.last || Null.new
end

def setup
def setup(_test)
@instances ||= []
mockery = new
mockery.logger = instance.logger unless @instances.empty?
Expand All @@ -48,8 +48,8 @@ def verify(*args)
instance.verify(*args)
end

def teardown
instance.teardown
def teardown(test)
instance.teardown(test)
ensure
@instances.pop
end
Expand Down Expand Up @@ -91,9 +91,10 @@ def verify(assertion_counter = nil)
end
end

def teardown
def teardown(test)
stubba.unstub_all
mocks.each(&:__expire__)
test_name = "#{test.class.name}##{test.name}"
mocks.each { |m| m.__expire__(test_name) }
reset
end

Expand Down
2 changes: 1 addition & 1 deletion test/acceptance/mock_test.rb
Original file line number Diff line number Diff line change
Expand Up @@ -159,7 +159,7 @@ def test_should_raise_stubbing_error_if_mock_receives_invocations_in_another_tes
reuse_mock_test_result = run_as_test do
Foo.logger.expects(:log).with('Foo was here')
e = assert_raises(Mocha::StubbingError) { Foo.new.use_the_mock }
assert e.message.include?('#<Mock:Logger> was instantiated in one test but it is receiving invocations within another test.')
assert e.message.include?('#<Mock:Logger> was instantiated in FakeTest#test_me but it is receiving invocations within another test.')
assert e.message.include?('This can lead to unintended interactions between tests and hence unexpected test failures.')
assert e.message.include?('Ensure that every test correctly cleans up any state that it introduces.')
end
Expand Down
4 changes: 4 additions & 0 deletions test/test_runner.rb
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,10 @@ def run_as_test(&block)
def run_as_tests(methods = {})
base_class = Mocha::TestCase
test_class = Class.new(base_class) do
def self.name
"FakeTest"
end

include Assertions

methods.each do |(method_name, proc)|
Expand Down
4 changes: 2 additions & 2 deletions test/unit/class_methods_test.rb
Original file line number Diff line number Diff line change
Expand Up @@ -6,12 +6,12 @@

class ClassMethodsTest < Mocha::TestCase
def setup
Mocha::Mockery.setup
Mocha::Mockery.setup(self)
@klass = Class.new.extend(Mocha::ClassMethods, Mocha::ObjectMethods)
end

def teardown
Mocha::Mockery.teardown
Mocha::Mockery.teardown(self)
end

def test_should_build_any_instance_object
Expand Down
16 changes: 8 additions & 8 deletions test/unit/mockery_test.rb
Original file line number Diff line number Diff line change
Expand Up @@ -7,15 +7,15 @@ class MockeryTest < Mocha::TestCase
include Mocha

def setup
Mockery.setup
Mockery.setup(self)
end

def teardown
Mockery.teardown
Mockery.teardown(self)
end

def test_should_return_null_mockery_if_not_setup
Mockery.teardown
Mockery.teardown(self)
mockery = Mockery.instance
assert_not_nil mockery
assert_kind_of Mockery::Null, mockery
Expand All @@ -35,7 +35,7 @@ def test_should_cache_instance_of_mockery

def test_should_expire_mockery_instance_cache
mockery1 = Mockery.instance
Mockery.teardown
Mockery.teardown(self)
mockery2 = Mockery.instance
assert_not_same mockery1, mockery2
end
Expand All @@ -55,7 +55,7 @@ def test_should_reset_list_of_mocks_on_teardown
mockery = Mockery.new
mock = mockery.unnamed_mock
mock.expects(:my_method)
mockery.teardown
mockery.teardown(self)
assert_nothing_raised(ExpectationErrorFactory.exception_class) { mockery.verify }
end

Expand All @@ -68,7 +68,7 @@ def test_should_build_instance_of_stubba_on_instantiation
def test_should_build_new_instance_of_stubba_on_teardown
mockery = Mockery.new
stubba1 = mockery.stubba
mockery.teardown
mockery.teardown(self)
stubba2 = mockery.stubba
assert_not_same stubba1, stubba2
end
Expand All @@ -83,7 +83,7 @@ def test_should_build_and_store_new_state_machine
def test_should_reset_list_of_state_machines_on_teardown
mockery = Mockery.new
mockery.new_state_machine('state-machine-name')
mockery.teardown
mockery.teardown(self)
assert_equal 0, mockery.state_machines.length
end

Expand All @@ -101,7 +101,7 @@ def test_should_unstub_all_methods_on_teardown
mockery = Mockery.new
stubba = mockery.stubba
stubba.stub(FakeMethod.new)
mockery.teardown
mockery.teardown(self)
assert stubba.stubba_methods.empty?
end

Expand Down
4 changes: 2 additions & 2 deletions test/unit/object_methods_test.rb
Original file line number Diff line number Diff line change
Expand Up @@ -7,12 +7,12 @@

class ObjectMethodsTest < Mocha::TestCase
def setup
Mocha::Mockery.setup
Mocha::Mockery.setup(self)
@object = Object.new.extend(Mocha::ObjectMethods)
end

def teardown
Mocha::Mockery.teardown
Mocha::Mockery.teardown(self)
end

def test_should_build_mocha_referring_to_self
Expand Down

0 comments on commit 0fc6ed1

Please sign in to comment.