How to manually set dateCreated for a unit test in Grails

08 November 2013

The dateCreated property in a domain class in Grails is not easily mainpulated - which in production is a really nice design choise. But when you have to test something and would like to manually set the dateCreateed property, this creates a challenge.

There are a few posts on Stackoverflow, describing possible solutions, most of them temporarily disables  timestamping. This solution does not use event listeners/eventTriggeringInterceptor, but just messes with the metaClass of the objects.

 

Assuming we have a very simple domain class, in this case just storing a text string, and the dateCreated. 

package grails.test.weird.stuff

class TextLog {

	String text
	Date dateCreated

	static constraints = {

	}

	String toString() {
		"$text $dateCreated"
	}
}

If we were to test a method extracting all objects in an interval, the method would probably be using something like line 31, a dynamic finder. Generating proper test data can be done like described below.

The dateCreated for the domain objects are are replaced in line 17, but this is at first not stored in the database, so a second save call, with a dirty object and the dateCreated marked as well is necessary (lines 21-23). Doing this will save the desired dates, and the data is ready for testing :)

package grails.test.weird.stuff

import grails.test.mixin.TestFor
import spock.lang.Specification

@TestFor(TextLog)
class TextLogSpec extends Specification {

	void "Test showing how to explicitly set dateCreated"() {
		setup:
		Date start = new Date(113,11,1)
		(-5..30).each {
			TextLog textLog = new TextLog(text: "Text of log ${1}")

			Date dateCreatedForInstance = start.clone()
			dateCreatedForInstance.hours += it
			textLog.metaClass.getDateCreated = { -> dateCreatedForInstance }
			textLog.save(flush: true, failOnError: true)

			// Fakes an update on the object, making Gorm dump it to the database
			textLog.dateCreated = textLog.dateCreated
			textLog.text = "${textLog.text}" 
			textLog.save(flush: true, failOnError: true)
		}

		when:
		Date end = start.clone()
		end.date++

		then:
		TextLog.findAllByDateCreatedBetween(start,end).size() == 25
	}
}

In this example, we generate objects with one hour intervals, reaching both before and after the full day we wish to obtain TextLog's for