Combining mockDomain() and mockFor() in Grails

As we’ve mentioned before, anything you can do to make automated testing easier in your Grails project will help you achieve one of the primary goals of the platform – high productivity.

Since you’ve chosen the Grails platform, you’re likely making good use of its features such as GORM, plugins, convention based spring wiring and Config.groovy/ConfigurationHolder. These all help you get to the business of writing your code without having to deal with plumbing all the time. Unfortunately the byproduct is that these all can make automated testing quite painful. Many times you end up going with integration tests simply because these give you access to the free plumbing even though you really just need to write unit tests. I won’t bother here debating and comparing unit vs. integration tests since this has been discussed many times before. We’ll just assume we want to write unit tests.

Grails (assuming 1.3.7) does provide the mockDomain(class, [instances]) which gives you an available “database” of objects in memory as provided to the mockDomain method without the need for a running container – perfect for unit tests. Unfortunately, as you start writing unit tests and encountering some issues, you come across this little gem in the documentation

… does not support the mocking of criteria or HQL queries

. That’s too bad really. You’d think they could easily support it, even if they just used an actual in memory database. Anyway, the documentation then adds

If you use either of those, simply mock the corresponding methods manually (for example with mockFor() ) or use an integration test with real data.

Sure, we could write an integration test, but we really don’t want to and shouldn’t have to. What about that mockFor() approach? Well you’re likely here because nobody has documented actually how to do so. We’re here to help. Here we’ll cover off combining mockDomain() with mockFor() for criteria in this post. In a future post, we’ll take a look at HQL queries.

It turns out to be not too bad. The use of closures leaves a little to be desired, but that’s not specific to this problem as it’s common to all mockFor(Domain).demand usage. Our example will assume you are using withCriteria to do something like find a range of data. We’ll also show you how you can provide other free behaviour via mockFor() – such as provided by a plugin.

We’ll have a domain class Order looking for orders placed within a specific date range and then provide mock behaviour for toDTO(dtoClass).

def order1 = new Order(orderId : 1, date : new Date(), amount : 19.99)
def order2 = new Order(orderId : 2, date : new Date(), amount : 39.99)
def order3 = new Order(orderId : 3, date : new Date(), amount : 49.99)
def order4 = new Order(orderId : 4, date : new Date(), amount : 99.99)
def orders = [order1, order2, order3, order4]
def mocker = mockFor(Order, true)
mocker.demand.toDTO(1..4) { clazz ->
    return new OrderDTO(orderId: delegate.orderId, date: delegate.date, amount: delegate.amount)
}
mocker.demand.static.withCriteria(1) {criteriaClosure ->
    def found = []
    orders.each { order ->
        if (criteriaClosure.from >= order.date && criteriaClosure <= order.date) {
            found << order
        }
    }
    return found
}
mockDomain(Order, [order1, order2, order3, order4])

Let's review what we've done. We create some test order instances for use both from our "database" and our mockFor / withCriteria logic. mockFor() gives us our handle to the mock that we can now set our expectations against since we're not really worried about testing against the mock and verifying its usage, we just need it to substitute for the database. toDTO is the free method you get with the DTO plugin. Use the mocker.demand as you would any grails mock. Then we substitute for the withCriteria code. Just make sure you use demand.static for withCriteria. We apply the logic against our collection of test instances (could just as easily have used the mocked instances available via mockDomain by doing Order.list().each) and now have a valid functioning "database" that supports criteria in unit tests.

Of course this doesn't actually test that your withCriteria is implemented correctly. The focus here was to establish an expected set of data to test against. You would have to write integration tests to actually test these types of methods on your model classes.

One thought on “Combining mockDomain() and mockFor() in Grails

  1. Pingback: Using mockFor() and HQL | Carfey Software Blog

Leave a Reply

Your email address will not be published. Required fields are marked *


six − 2 =

You may use these HTML tags and attributes: <a href="" title=""> <abbr title=""> <acronym title=""> <b> <blockquote cite=""> <cite> <code> <del datetime=""> <em> <i> <q cite=""> <strike> <strong>