Skip to content

Commit

Permalink
Merge pull request #327 from freerange/prevent-use-of-mocha-outside-test
Browse files Browse the repository at this point in the history
Prevent use of Mocha outside the context of a test/example
  • Loading branch information
floehopper committed Apr 6, 2018
2 parents c0f7b9d + 89eeb1c commit d6c6c82
Show file tree
Hide file tree
Showing 26 changed files with 206 additions and 24 deletions.
17 changes: 17 additions & 0 deletions lib/mocha/central.rb
Expand Up @@ -2,6 +2,23 @@ module Mocha

class Central

class Null < self

def initialize(&block)
super
@raise_not_initialized_error = block
end

def stub(*)
@raise_not_initialized_error.call
end

def unstub(*)
@raise_not_initialized_error.call
end

end

attr_accessor :stubba_methods

def initialize
Expand Down
17 changes: 17 additions & 0 deletions lib/mocha/error_with_filtered_backtrace.rb
@@ -0,0 +1,17 @@
require 'mocha/backtrace_filter'

module Mocha

# @private
class ErrorWithFilteredBacktrace < StandardError

# @private
def initialize(message = nil, backtrace = [])
super(message)
filter = BacktraceFilter.new
set_backtrace(filter.filtered(backtrace))
end

end

end
3 changes: 1 addition & 2 deletions lib/mocha/hooks.rb
Expand Up @@ -21,6 +21,7 @@ module Hooks
#
# This method should be called before each individual test starts (including before any "setup" code).
def mocha_setup
Mockery.setup
end

# Verifies that all mock expectations have been met (only for use by authors of test libraries).
Expand All @@ -37,8 +38,6 @@ 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
ensure
Mockery.reset_instance
end
end
end
1 change: 1 addition & 0 deletions lib/mocha/integration/mini_test/version_13.rb
Expand Up @@ -25,6 +25,7 @@ def run runner
begin
begin
@passed = nil
mocha_setup
self.setup
self.__send__ self.name
mocha_verify(assertion_counter)
Expand Down
1 change: 1 addition & 0 deletions lib/mocha/integration/mini_test/version_140.rb
Expand Up @@ -25,6 +25,7 @@ def run runner
begin
begin
@passed = nil
mocha_setup
self.setup
self.__send__ self.__name__
mocha_verify(assertion_counter)
Expand Down
1 change: 1 addition & 0 deletions lib/mocha/integration/mini_test/version_141.rb
Expand Up @@ -31,6 +31,7 @@ def run runner
begin
begin
@passed = nil
mocha_setup
self.setup
self.__send__ self.__name__
mocha_verify(assertion_counter)
Expand Down
1 change: 1 addition & 0 deletions lib/mocha/integration/mini_test/version_142_to_172.rb
Expand Up @@ -31,6 +31,7 @@ def run runner
begin
begin
@passed = nil
mocha_setup
self.setup
self.__send__ self.__name__
mocha_verify(assertion_counter)
Expand Down
1 change: 1 addition & 0 deletions lib/mocha/integration/mini_test/version_200.rb
Expand Up @@ -31,6 +31,7 @@ def run runner
begin
begin
@passed = nil
mocha_setup
self.setup
self.__send__ self.__name__
mocha_verify(assertion_counter)
Expand Down
1 change: 1 addition & 0 deletions lib/mocha/integration/mini_test/version_201_to_222.rb
Expand Up @@ -31,6 +31,7 @@ def run runner
begin
begin
@passed = nil
mocha_setup
self.setup
self.__send__ self.__name__
mocha_verify(assertion_counter)
Expand Down
1 change: 1 addition & 0 deletions lib/mocha/integration/mini_test/version_2110_to_2111.rb
Expand Up @@ -32,6 +32,7 @@ def run runner
begin
@passed = nil
self.before_setup
mocha_setup
self.setup
self.after_setup
self.run_test self.__name__
Expand Down
1 change: 1 addition & 0 deletions lib/mocha/integration/mini_test/version_2112_to_320.rb
Expand Up @@ -35,6 +35,7 @@ def run runner
begin
@passed = nil
self.before_setup
mocha_setup
self.setup
self.after_setup
self.run_test self.__name__
Expand Down
1 change: 1 addition & 0 deletions lib/mocha/integration/mini_test/version_230_to_2101.rb
Expand Up @@ -31,6 +31,7 @@ def run runner
begin
begin
@passed = nil
mocha_setup
self.setup
self.run_setup_hooks
self.__send__ self.__name__
Expand Down
1 change: 1 addition & 0 deletions lib/mocha/integration/test_unit/gem_version_200.rb
Expand Up @@ -26,6 +26,7 @@ def run(result)
yield(Test::Unit::TestCase::STARTED, name)
begin
begin
mocha_setup
run_setup
__send__(@method_name)
mocha_verify(assertion_counter)
Expand Down
1 change: 1 addition & 0 deletions lib/mocha/integration/test_unit/gem_version_201_to_202.rb
Expand Up @@ -26,6 +26,7 @@ def run(result)
yield(Test::Unit::TestCase::STARTED, name)
begin
begin
mocha_setup
run_setup
run_test
mocha_verify(assertion_counter)
Expand Down
1 change: 1 addition & 0 deletions lib/mocha/integration/test_unit/gem_version_203_to_220.rb
Expand Up @@ -26,6 +26,7 @@ def run(result)
yield(Test::Unit::TestCase::STARTED, name)
begin
begin
mocha_setup
run_setup
run_test
mocha_verify(assertion_counter)
Expand Down
1 change: 1 addition & 0 deletions lib/mocha/integration/test_unit/gem_version_230_to_250.rb
Expand Up @@ -28,6 +28,7 @@ def run(result)
yield(Test::Unit::TestCase::STARTED_OBJECT, self)
begin
begin
mocha_setup
run_setup
run_test
run_cleanup
Expand Down
Expand Up @@ -25,6 +25,7 @@ def run(result)
@_result = result
begin
begin
mocha_setup
setup
__send__(@method_name)
mocha_verify(assertion_counter)
Expand Down
Expand Up @@ -25,6 +25,7 @@ def run(result)
@_result = result
begin
begin
mocha_setup
setup
__send__(@method_name)
mocha_verify(assertion_counter)
Expand Down
41 changes: 38 additions & 3 deletions lib/mocha/mockery.rb
Expand Up @@ -7,16 +7,46 @@
require 'mocha/logger'
require 'mocha/configuration'
require 'mocha/stubbing_error'
require 'mocha/not_initialized_error'
require 'mocha/expectation_error_factory'

module Mocha

class Mockery

class Null < self

def add_mock(*)
raise_not_initialized_error
end

def add_state_machine(*)
raise_not_initialized_error
end

def stubba
Central::Null.new(&method(:raise_not_initialized_error))
end

private

def raise_not_initialized_error
message = 'Mocha methods cannot be used outside the context of a test'
raise NotInitializedError.new(message, caller)
end

end

class << self

def instance
@instance ||= new
instances.last || Null.new
end

def setup
mockery = new
mockery.logger = instance.logger unless instances.empty?
@instances.push(mockery)
end

def verify(*args)
Expand All @@ -25,10 +55,15 @@ def verify(*args)

def teardown
instance.teardown
ensure
@instances.pop
@instances = nil if instances.empty?
end

def reset_instance
@instance = nil
private

def instances
@instances ||= []
end

end
Expand Down
9 changes: 9 additions & 0 deletions lib/mocha/not_initialized_error.rb
@@ -0,0 +1,9 @@
require 'mocha/error_with_filtered_backtrace'

module Mocha

# Exception raised when Mocha has not been initialized, e.g. outside the
# context of a test.
class NotInitializedError < ErrorWithFilteredBacktrace; end

end
13 changes: 2 additions & 11 deletions lib/mocha/stubbing_error.rb
@@ -1,19 +1,10 @@
require 'mocha/backtrace_filter'
require 'mocha/error_with_filtered_backtrace'

module Mocha

# Exception raised when stubbing a particular method is not allowed.
#
# @see Configuration.prevent
class StubbingError < StandardError

# @private
def initialize(message = nil, backtrace = [])
super(message)
filter = BacktraceFilter.new
set_backtrace(filter.filtered(backtrace))
end

end
class StubbingError < ErrorWithFilteredBacktrace; end

end
2 changes: 0 additions & 2 deletions test/acceptance/acceptance_test_helper.rb
Expand Up @@ -27,13 +27,11 @@ def setup_acceptance_test
Mocha::Configuration.reset_configuration
@logger = FakeLogger.new
mockery = Mocha::Mockery.instance
@original_logger = mockery.logger
mockery.logger = @logger
end

def teardown_acceptance_test
Mocha::Configuration.reset_configuration
Mocha::Mockery.instance.logger = @original_logger
end

include Introspection::Assertions
Expand Down
79 changes: 79 additions & 0 deletions test/acceptance/prevent_use_of_mocha_outside_test_test.rb
@@ -0,0 +1,79 @@
require File.expand_path('../acceptance_test_helper', __FILE__)
require 'mocha/setup'
require 'mocha/not_initialized_error'

class PreventUseOfMochaOutsideTestTest < Mocha::TestCase

include AcceptanceTest

def setup
setup_acceptance_test
mocha_teardown
end

def teardown
teardown_acceptance_test
end

def test_should_raise_exception_when_mock_called_outside_test
assert_raises(Mocha::NotInitializedError) { mock('object') }
end

def test_should_raise_exception_when_stub_called_outside_test
assert_raises(Mocha::NotInitializedError) { stub('object') }
end

def test_should_raise_exception_when_stub_everything_called_outside_test
assert_raises(Mocha::NotInitializedError) { stub_everything('object') }
end

def test_should_raise_exception_when_states_called_outside_test
assert_raises(Mocha::NotInitializedError) { states('state-machine') }
end

def test_should_raise_exception_when_expects_called_on_instance_outside_test
instance = Class.new.new
assert_raises(Mocha::NotInitializedError) { instance.expects(:expected_method) }
end

def test_should_raise_exception_when_expects_called_on_class_outside_test
klass = Class.new
assert_raises(Mocha::NotInitializedError) { klass.expects(:expected_method) }
end

def test_should_raise_exception_when_expects_called_on_any_instance_outside_test
klass = Class.new
assert_raises(Mocha::NotInitializedError) { klass.any_instance.expects(:expected_method) }
end

def test_should_raise_exception_when_stubs_called_on_instance_outside_test
instance = Class.new.new
assert_raises(Mocha::NotInitializedError) { instance.stubs(:expected_method) }
end

def test_should_raise_exception_when_stubs_called_on_class_outside_test
klass = Class.new
assert_raises(Mocha::NotInitializedError) { klass.stubs(:expected_method) }
end

def test_should_raise_exception_when_stubs_called_on_any_instance_outside_test
klass = Class.new
assert_raises(Mocha::NotInitializedError) { klass.any_instance.stubs(:expected_method) }
end

def test_should_raise_exception_when_unstub_called_on_instance_outside_test
instance = Class.new.new
assert_raises(Mocha::NotInitializedError) { instance.unstub(:expected_method) }
end

def test_should_raise_exception_when_unstub_called_on_class_outside_test
klass = Class.new
assert_raises(Mocha::NotInitializedError) { klass.unstub(:expected_method) }
end

def test_should_raise_exception_when_unstub_called_on_any_instance_outside_test
klass = Class.new
assert_raises(Mocha::NotInitializedError) { klass.any_instance.unstub(:expected_method) }
end

end
9 changes: 5 additions & 4 deletions test/unit/hooks_test.rb
@@ -1,10 +1,11 @@
require File.expand_path('../../test_helper', __FILE__)
require 'mocha/hooks'
require 'mocha/mockery'

class HooksTest < Mocha::TestCase
class Mocha::Mockery
class << self
attr_writer :instance
attr_writer :instances
end
end

Expand All @@ -19,11 +20,11 @@ def teardown

def test_ensure_mockery_instance_is_reset_even_when_an_exception_is_raised_in_mockery_teardown
fake_test_case = Object.new.extend(Mocha::Hooks)
original_mockery = FakeMockery.new
Mocha::Mockery.instance = original_mockery
mockery = FakeMockery.new
Mocha::Mockery.instances = [mockery]

fake_test_case.mocha_teardown rescue nil

assert_not_same Mocha::Mockery.instance, original_mockery
assert_kind_of Mocha::Mockery::Null, Mocha::Mockery.instance
end
end

0 comments on commit d6c6c82

Please sign in to comment.