DEV Community

Nancy Deschenes
Nancy Deschenes

Posted on

Grails: Mocking domain objects/criteria that reference Joda Time objects

I can rant about the difficulties of handling time correctly when programming like anyone else who has ever tried. I can also criticize the default date and time objects in Java, but I will just say this: Just use Joda Time. You can also take a peek at an article I wrote a while back for more details. I even use Joda Time in my domain objects in Grails. It requires a small amount of configuration, but it is well worth it. See the plugin for more details.

Where things get a bit more complicated is when I want to test my GORM criteria when the criteria involves an org.joda.time.DateTime. For example:

/** My domain object */
class Membership {
  User user
  DateTime memberSince
}

/** My service */
class MembershipService {
  boolean transactional = true

  /** Return users who have been around for at least 5 years */
  List<User> findOldUsers() {
    Membership.createCriteria().list() {
      user {
        eq('deleted', false)
      }
      lte('memberSince', new DateTime().minusYears(5))
    }
  }
}
Enter fullscreen mode Exit fullscreen mode

Now, the test:

@Mock([Membership, User])
@TestFor(MembershipService)
class MembershipServiceSpec extends Specification {
  def "findOldUsers does not return anything if all the users have been around less than a year"() {
    given:
      User u1 = new User().save() 
      // or save(false) if there are constraints we want to ignore for the time being)
      new Membership(user: user, memberSince: new DateTime().minusMonths(4)).save();

    expect:
      service.findOldUsers().isEmpty()
  }
}
Enter fullscreen mode Exit fullscreen mode

What happens? well....

java.lang.IllegalArgumentException: Property [memberSince] is not a valid property of class [com.mycompany.Membership]
Enter fullscreen mode Exit fullscreen mode

Of course, the GORM mocking framework doesn't know about DateTime, so it didn't register it as a property of Membership. To fix this, all we need to do is, first tell the mocking framework about Joda Time classes, and second, we have to mock our domain objects after we mock the Joda Time classes.

import grails.plugin.jodatime.simpledatastore.SimpleMapJodaTimeMarshaller
@Mock(User)
@TestFor(MembershipService)
class MembershipServiceSpec extends Specification {
  setup() {
    SimpleMapJodaTimeMarshaller.initialize()
    mockDomain(Membership)
  }

  // [...]
}    
Enter fullscreen mode Exit fullscreen mode

Discussion (0)