Mocha Configuration
Posted by James Mead Mon, 09 Feb 2009 11:40:00 GMT
Introduction
In his recent look at Mocha’s internals, Brian Guthrie mentioned that he hadn’t realised it was possible to configure Mocha to warn or disallow mocking of non-existent methods. So I thought it was time I explained Mocha’s configuration settings. These configuration settings are all somewhat experimental and feedback on their effectiveness would be welcome.
There are 3 levels for all of the existing conditions :-
# default behaviour
Mocha::Configuration.allow(condition)
# warning is displayed when condition is met
Mocha::Configuration.warn_when(condition)
# error is raised when condition is met
Mocha::Configuration.prevent(condition)Stubbing a non-existent method
This is useful if you want to ensure that methods you’re mocking really exist. A common criticism of unit tests with mock objects is that such a test may (incorrectly) pass when an equivalent non-mocking test would (correctly) fail. While you should always have some integration tests, particularly for critical business functionality, this Mocha configuration setting should catch scenarios when mocked methods and real methods have become misaligned.
Mocha::Configuration.prevent(:stubbing_non_existent_method)
class Example
end
class ExampleTest < Test::Unit::TestCase
def test_example
example = Example.new
example.stubs(:method_that_doesnt_exist)
# => Mocha::StubbingError: stubbing non-existent method:
# => #<Example:0x593760>.method_that_doesnt_exist
end
endStubbing a method unnecessarily
This is useful for identifying unused stubs. Unused stubs are often accidentally introduced when code is refactored.
Mocha::Configuration.prevent(:stubbing_method_unnecessarily)
class ExampleTest < Test::Unit::TestCase
def test_example
example = mock('example')
example.stubs(:unused_stub)
# => Mocha::StubbingError: stubbing method unnecessarily:
# => #<Mock:example>.unused_stub(any_parameters)
end
endStubbing a non-public method
Many people think that it’s good practice only to mock public methods. This is one way to prevent your tests being too tightly coupled to the internal implementation of a class. Such tests tend to be very brittle and not much use when refactoring.
Mocha::Configuration.prevent(:stubbing_non_public_method)
class Example
def internal_method; end
private :internal_method
end
class ExampleTest < Test::Unit::TestCase
def test_example
example = Example.new
example.stubs(:internal_method)
# => Mocha::StubbingError: stubbing non-public method:
# => #<Example:0x593530>.internal_method
end
endStubbing Method on Non-Mock Object
If you like the idea of mocking roles not objects and you don’t like stubbing concrete classes, this is the setting for you. However, while this restriction makes a lot of sense in Java with its explicit interfaces, it may be moot in Ruby where roles are probably best represented as Modules. Anyway that’s probably a discussion for another day.
Mocha::Configuration.prevent(:stubbing_method_on_non_mock_object)
class Example
def example_method; end
end
class ExampleTest < Test::Unit::TestCase
def test_example
example = Example.new
example.stubs(:example_method)
# => Mocha::StubbingError: stubbing method on non-mock object:
# => #<Example:0x593620>.example_method
end
end
Thanks!
Now I can get rid of all my “quacks_like”s, except where I need them.