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.
1 thought on “Combining mockDomain() and mockFor() in Grails”
Comments are closed.