Hamlet D'Arcy has been writing software for over a decade, and has spent considerable time coding in C++, Java, and Groovy. He's passionate about learning new languages and different ways to think about problems, and recently he's been discovering the joys of both F# and Scheme. He's an active member of the Groovy Users of Minnesota and the Object Technology User Group, is a committer on the Groovy project, and is a contributor on a few open source projects (including JConch and the IDEA Groovy Plugin). He blogs regularly at http://hamletdarcy.blogspot.com and can be found on Twitter as HamletDRC (http://twitter.com/hamletdrc). Hamlet is a DZone MVB and is not an employee of DZone and has posted 28 posts at DZone. You can read more from them at their website. View Full User Profile

New in Groovy 1.7.1: Constructor Mocking and Half Mocks

02.08.2010
| 10360 views |
  • submit to reddit

The 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!

From http://hamletdarcy.blogspot.com

Published at DZone with permission of Hamlet D'Arcy, author and DZone MVB.

(Note: Opinions expressed in this article and its replies are the opinions of their respective authors and not those of DZone, Inc.)

Tags: