New in Groovy 1.7.1: Constructor Mocking and Half Mocks
Join the DZone community and get the full member experience.
Join For FreeThe Groovy MockFor object got some fun new features this weekend: constructor mocking and "half-mocks". The tickets are marked for Groovy 1.7.1, so these features are available in the nightly builds or in the next few weeks as 1.7.1 is released. The easiest thing is, of course, to just build from source.
The
first feature is Constructor mocking. It is now possible to specify a
return value for mock constructor calls. Here is an example that uses a
"Person" object. The "tom" variable is a real Person object, while the
"mary" object is not. Thanks to this new feature, instantiating Mary
returns to you Tom.
import groovy.mock.interceptor.MockFor
class Person {
String name
}
def tom = new Person(name:'Tom')
def mock = new MockFor(Person, true)
mock.demand.with {
Person() { tom }
}
mock.use {
def mary = new Person(name:'Mary')
assert tom == mary
}
There
are two parts to getting this working: the "demand.with" block, where
the person constructor is specified to return tom, and the true
parameter passed to MockFor. To make MockFor backwards compatible, a
flag was needed to control when to allow constructor mocking and when
not to allow it. As you can see, after doing this, the tom and mary
objects are equal (in fact, the same reference).
The problem
with this feature alone is that calling tom.getName() results in a mock
exception saying no demand is set. If Tom is a real instance of Person
then why would you get a demand exception? Well, within the mock.use
block Tom is a mock. The
behaviour you probably expect is to have the real Tom methods pass
through to the real implementation, which is why "half-mocks" were
introduced. Check out how you can specify methods to ignore and pass
through to the underlying object:
mock.ignore(~'get.*')
mock.demand.with {
Person() { tom }
}
mock.use {
def mary = new Person(name:'Mary')
assert mary.name == 'Tom'
}
The
ignore method takes a pattern to match, and the mock ignores those
methods, passing them on to the underlying implementation. So your mock
is still a mock, not a real object, but methods are passed through as
needed. This is the opposite of how EasyMock partial mocking works,
where a partial mock is technically a subclass of the target class, and
methods are routed by default to the real implementation. In Groovy, the method are routed by default to the mock.
Fun stuff, and thanks to Paul King for the work!
Opinions expressed by DZone contributors are their own.
Comments