How to create Makers: Mocking domain objects for testing (Groovy)

General Explanation:
This will explain how to make 'Maker(s)' objects to properly mock out groovy domain objects. The concept of a maker object is that you can call it from either your unit or integration tests to setup and create test objects for you so that you do not have to create new objects for each and every test. You can also call makers from within makers to ensure you have mock data all the way through your mock object. Makers are written as groovy classes as opposed to grails domain classes and our generally placed under the Sources:groovy in a makers directory.

First thing first for our new maker we need to ensure we add it to the correct pacakge. package com.your.domain.makers This ensures that all your makers are contained in the same location for ease of access. Next thing that we will need is any imports that maybe required in order to build our mock object. At minimum we will always need at least one import and that will be importing the domain object that we will be mocking. import com.your.domain.MyObject Next up we will create our maker class which will contain the logic that will generate our mock object. class MyObjectMaker { ... } Within our maker class we will have two methods an 'init' method and a 'add' method. I will be perfectly honest and say I am not 100% certain on how the init works but it helps ensure that the correct things are mocked in the proper order to my understanding. Many times the init wil only need to be set like this.

Option 1: Single Maker Setup
static def init(test) { DomainMakerUtils.mockDomainIfApplicable(test, MyObject) } Other times if there seem to be issues with multiple makers or items getting instantiated in the correct order the init may need to be set up like this.

Option 2: Multiple Makers Setup
static def init(test) { if (DomainMakerUtils.mockDomainIfApplicable(test, MyObject)) { OtherMaker.init(test) } } Once your init method is setup you will need to create your add method. The add method will be static just as the init so that it can be called from anywhere without the need to instantiate a maker object. static MyObject add(pParams) { ... } After we have entered the add we will add the logic neccessary for building a mocked object. First we will want to get the incoming params into their own object.

Note: This params and the pParams object are not the same as the standard params object in groovy/grails; it does not contain the same methods or method signatures. ​def params = DomainMakerUtils.getparamsForClass(pParams, MyObject) Next we will check to see if we are running an integration test or a unit test and if the neccessary params are populated. This is important as if we are running an integration test the maker just needs to search for and return an object based on criteria if that object exists; if the object doesn't exist or this is a unit test the maker will create a new object for the test. If we are working with standard grails scaffolding it will most likely look something like this.

Option 1: Standard Scaffolding
if (DomainMakerUtils.thisIsAnIntegrationTest && params.id) { def myObject = MyObject.findById(params.id) if (myObject) { return myObject } } If we are dealing with non-standard scaffolding or a composit id we may have to validate that more than one parameter is actually populated in which case this check will look more like this.

Option 2: Non-Standard Scaffolding or Composit Id/Key
if (DomainMakerUtils.thisIsAnIntegrationTest && params.idP1 && params.idP2) { def myObject = MyObject.findByP1AndP2(params.idP1 && params.idP2) if (myObject) { return myObject } } Both of the preceeding options make use of the dynamic finders that are part of the grails framework and make locating objects within the database very easy. Once this check is complete and it is either not an integration test or the object does not exist it is time to create a new object to test against. MyObject myTestObject  = new MyObject myTestObject.firstName = params.firstName ?: 'Steve' myTestObject.lastName  = params.lastName  ?: 'Smith' myTestObject.phone     = params.phone     ?: 5555555555 myTestObject.state     = params.state     ?: 'WI' myTestObject.properties = params mytestObject.save(flush: true, failOnError: true) So to clearly explain our new object, we first create a new instance of our object, then we want to assign values into the fields so we have data to test against. It is important to setup the object so that it can take either a passed in value or have a default so that we can either add our own specific values later at object creation time or we can just use the default object. The elvis operator is stating that we want to populate the fields using the params value first but if the params value is null default to the provided value. Following the assignment we add the entire params object to the myTestObject.properties to ensure that any other settings are passed through and then we save our object. We have added the 'failOnError: true' flag to make sure we are alerted if a problem occures durring our object creation.

Complete Code Examples:
Here are two complete examples of each maker so that each is clearly displayed and understood.

Option 1: Single Maker Code
package com.your.domain.makers import com.your.domain.MyObject class MyObjectMaker { static def init(test) { DomainMakerUtils.mockDomainIfApplicable(test, MyObject) }    static MyObject add(pParams) { def params = DomainMakerUtils.getParamsForClass(pParams, MyObject) if (DomainMakerUtils.thisIsAnIntegrationTest && params.id) { def myObject = MyObject.findById(params.id) if (myObject) { return myObject }        }         MyObject myTestObject   = new MyObject myTestObject.firstName = params.firstName ?: 'Steve' myTestObject.lastName  = params.lastName  ?: 'Smith' myTestObject.phone     = params.phone     ?: 5555555555 myTestObject.state     = params.state     ?: 'WI' myTestObject.properties = params myTestObject.save(flush: true, failOnError: true) } }

Option 2: Composit Id/Key Maker Code
package com.your.domain.makers import com.your.domain.MyObject class MyObjectMaker { static def init(test) { DomainMakerUtils.mockDomainIfApplicable(test, MyObject) }    static MyObject add(pParams) { def params = DomainMakerUtils.getParamsForClass(pParams, MyObject) if (DomainMakerUtils.thisIsAnIntegrationTest && params.idP1 && params.idP2) { def myObject = MyObject.findByP1AndP2(params.idP1 && params.idP2) if (myObject) { return myObject }        }         MyObject myTestObject   = new MyObject myTestObject.firstName = params.firstName ?: 'Steve' myTestObject.lastName  = params.lastName  ?: 'Smith' myTestObject.phone     = params.phone     ?: 5555555555 myTestObject.state     = params.state     ?: 'WI' myTestObject.properties = params myTestObject.save(flush: true, failOnError: true) } }