Tuesday, 29 July 2008

Where did the Response go in 1.0.3?

We noticed that in the move from Grails 1.0.2 to 1.0.3, the response object inside Spring's RequestContextHolder has seemingly gone but it looks like it's always been somewhere else:

So:

def response = org.springframework.web.context.request.RequestContextHolder.requestAttributes.response

Becomes:

def response = org.springframework.web.context.request.RequestContextHolder.requestAttributes.currentResponse



Worth keeping an eye out for.

Thursday, 24 July 2008

Experimental testing plugin

Saw this post on the grails mailing from a grails committer, Peter Ledbrook, might be worth checking out:

Hi everyone,

I have been working on some improvements to the Grails testing
framework recently which will go into Grails 1.1. Some of the support
classes though will work with Grails 1.0.x, so I have packaged them up
as a plugin. You can install it using:

grails install-plugin testing

Before you rush to download it, please be warned that it is nowhere
near a complete implementation yet, particularly
ControllerUnitTestCase. However, I would like to solicit feedback
early on so that we can really nail the problems that people are
having in testing. And if you can supply patches, great!

So, what lurks in the plugin? I'm planning to get some documentation
up soon, but for now I'll cover some common use cases. Before that,
the current set of classes are designed to be used in unit tests. As a
general trend, we want to encourage people to write unit tests in
preference to integration tests. We also want to make sure that unit
tests can be run from within an IDE, i.e. there should be no
requirement on a running Grails instance.

For the first example I'll show you how to test domain constraints.
Domain classes often lack logic, and so they don't get tested.
However, plenty of errors can creep in to the constraints so it's
worth validating them.

class MyDomain {
String name
Integer age

static constraints = {
name(nullable: false, blank: false)
age(nullable: false, min: 10, max: 100)
}
}

class MyDomainUnitTests extends GrailsUnitTestCase {
void testConstraints() {
// Mock the validate() method.
registerMetaClass(MyDomain)
MockUtils.
prepareForConstraintsTests(MyDomain)

// Test that a fresh new domain instance fails validation on
// the "nullable: false" constraints.
def testInstance = new MyDomain()
def errors = testInstance.validate()
assertEquals 2, errors.size()
assertEquals "nullable", errors["name"]
assertEquals "nullable", errors["age"]

// Test the other constraints
testInstance = new MyDomain(name: " ", age: 5)
errors = testInstance.validate()
assertEquals 2, errors.size()
assertEquals "blank", errors["name"]
assertEquals "min", errors["age"]
}
}

The main things to note here are:

1. We sub-class GrailsUnitTestCase
2. "registerMetaClass()" and "MockUtils.prepare...()" add the
validate() method to the domain class
3. We create instances of the domain class, call validate(), and check
whether any errors were found

On (2), the two lines will be replaced by a method on
GrailsUnitTestCase in the near future. On (3), the validate() method
returns a map of validation errors. Note that we check for the name of
the constraint, not the i18n error code associated with the
constraint.

That's it for domain constraints. For other unit tests, such as for
services and controllers, GrailsUnitTestCase provides the method
"mockDomain(Class, List)":

void testMethod() {
mockDomain(MyDomain, [
new MyDomain(name: "John Smith", age: 35),
new MyDomain(name: "Alice Smith", age: 64),
new MyDomain(name: "Irene Pane", age: 22),
new MyDomain(name: "Patrick Rose", age: 45) ])

def testService = new MyService()
testService.doSomething()
...
}

The method injects working versions of the dynamic methods and
properties that normally go with domain classes, in particular the
dynamic finders. Where appropriate, these injected methods/properties
use the given list of domain instances as a source of data. For
example, if MyService.doSomething called a dynamic finder like this:

MyDomain.findByNameLike("%Smith")

the mock property would return a list containing the "John Smith" and
"Alice Smith" MyDomain instances in that order. Another useful method
is "mockFor()" which returns an object that you can use pretty much
like the Groovy MockFor class:

def mockControl = mockFor(MyDomain)
mockControl.demand.save(1..1) {-> return true}
mockControl.demand.static.findByName(1..1) { name -> return [] }

The best thing about this method is that it works seemlessly with
"mockDomain()", i.e. you can readily override the methods provided by
"mockDomain()" via the mock object returned by "mockFor()".

Finally, there is a ControllerUnitTestCase, but it is in the very
early stages of development. I recommend you only use it if you're
willing to patch it up with the functionality you need. It will
automatically inject all the normal controller properties and methods,
but not much else. However, one nice feature I have implemented
already is the ability to set the body of the request to some XML
(either a string or builder markup), particularly useful for REST
controllers based on XML. I need to add support for JSON too.

That's it for now. I would certainly give GrailsUnitTestCase a go
because I have already found it much easier to write Grails unit tests
than I used to. If you want to raise issues or provide patches, please
add them to the main Grails JIRA, setting the fix version to 1.1 and
assigning them to me (username "pledbrook").

Cheers,
Glenn

Saturday, 19 July 2008

Does the noise in my head bother you?

A quick post for those of you using the dynamic addTo* methods with manyToMany relationships under grails 1.0.3. You may notice alot of noise in your logs / test results - this is down to a stray println in DomainClassGrailsPlugin.groovy (line 173) which does the following:

println "$obj - ${obj.properties} - ${prop.otherSide.name}"

Options: patch grails, wait for 1.0.4, ignore it.

Tuesday, 15 July 2008

Tricking Grails into persisting Enum properties

Although Grails 1.0.3 implements persistence of java.lang.Enum fields in GORM, unfortunately it sticks with the misguided Hibernate convention of persisting the Enum ordinal rather than the String value. In Hibernate at least you can override this default, in Grails currently you can't.

In the meantime, here's a workaround pattern that should be amenable to a low-impact refactor once Grails 1.0.4 drops.

    enum Flavr { /* some enum instances */ }

class Lolcat {
String flavrName

static transients = ['flavr']

static constraints = {
flavrName(nullable: true, inList: Flavr.values().collect{it.name()})
}

static mapping = {
columns {
flavrName(name: 'flavr')
}
}

Flavr getFlavr() {
return flavrName ? Flavr.valueOf(flavrName) : null
}

void setFlavr(Flavr flavr) {
flavrName = flavr?.name()
}
}


What we've done is create a transient Enum property backed by a String that GORM uses to persist the value. The constraint prevents any erroneous values being set directly to the String property. The column name mapping is an attempt at future proofing, so the column name won't need to be changed once 'flavr' becomes a full-blown persistent Enum.

As and when that's possible you simply replace 'String flavrName' with a 'Flavr flavr' and remove the transients, constraints and mapping blocks and the getter and setter.

Two downsides are 1) that dynamic finders and criteria queries must operate on real properties and not transient ones and 2) that you can pretty much guarantee some muppet will directly access 'flavrName' somewhere - something you can guard against much more effectively in Java than you can in Groovy.

Thursday, 10 July 2008

Groovy operators

Max suggested I post something about this, so...

Groovy has some operators that vanilla Java doesn't. Here's a quick reference:

<=> (Spaceship)
Example:
a <=> b is equivalent to a.compareTo(b)

?: (Elvis)
Example:
x ?: y is equivalent to x != null ? x : y

=~ (Find)
Example:
Matcher m = "abc" =~ /a/

==~ (Match)
Example:
assert "abc" ==~ /\w+/ Note this is a full match not a partial.

~ (Create pattern)
Example:
~/abc/ is equivalent to new Pattern(/abc/)

*. (Spread)
Example:
a*.b is equivalent to a.collect { it.b }

.& (Method reference)
Example:
Closure c = a.&b gives you a reference to the method "b()" on object "a" that you can pass around as a Closure.

.@ (Property access)
Example:
def c = a.@b gives you the value of the property "b" on object "a" directly (i.e. without going through the getter). This can be useful on horrible classes like java.awt.Dimension where the type of a public property is different to the return type of its getter. Most other uses of it are probably best avoided.

?. (Null-safe dereference)
Example:
a?.b is equivalent to a == null ? null : a.b

as (Type coercion)
Example:
new Date() as String is equivalent to new Date().toString() which is a trivial example but you can do quite exciting type conversions by overriding asType.

is (Object identity)
Example:
a is b is equivalent to a == b in Java.

kthxbye.

Tuesday, 8 July 2008

Ooops Mocks

Why do this ...
void testOne() {
def result
mockServiceA.use {
mockServiceB.use {
mockServiceC.use {
result = testMe.someMethod('a', 'b')
}
}
}
assertEquals(1, result)
}

void testTwo() {
def result
mockServiceA.use {
mockServiceB.use {
mockServiceC.use {
result = testMe.someOtherMethod('a', 'b')
}
}
}
assertEquals(1, result)
}


When you can do this...

void testOne() {
def result = executeWithMocks { testMe.someMethod('a', 'b') }
assertEquals(1, result)
}

void testTwo() {
def result = executeWithMocks { testMe.someOtherMethod('a', 'b') }
assertEquals(1, result)
}

private Object executeWithMocks(Closure closure) {
def result
mockServiceA.use {
mockServiceB.use {
mockServiceC.use {
result = closure()
}
}
}
return result
}

Friday, 4 July 2008

Criteria queries + associations = confusion


Since we've been bitten by this problem twice in the same week now I thought it was worth a post.

Consider these classes:
    class Author {
String name
static hasMany = [books: Book]
}

class Book {
String name
static belongsTo = [author: Author]
}

Then let's attempt to find all authors who have written a book with a particular name:
    Author wg = new Author(name: 'William Gibson')
wg.addToBooks new Book(name: 'Pattern Recognition')
wg.addToBooks new Book(name: 'Spook Country')
assert wg.save(flush: true)

sessionFactory.currentSession.clear()

List authors = Author.withCriteria {
books {
eq('name', 'Spook Country')
}
}
assertEquals 1, authors.size()
assertEquals 2, authors[0].books.size() // FAILS result is 1

When you look at how Hibernate implements this query under the hood it's reasonably clear what's going on. It will be doing something roughly along the lines of:
    select * from author a, book b where a.id = b.author_id and b.name = ?

The problem is it then makes the mistake of thinking that the books collections of the author objects it has found can be initialised based on that result set.

Where this problem has bitten us is when objects fetched by such a query are also used by an unrelated bit of code that doesn't bother reading from the database as it quite reasonably thinks (some of) the objects is needs are in the Hibernate session already. Unfortunately, the view module related to this other bit of code is reliant on the elements of the collection that weren't loaded by our defective query. I'm sure you can imagine the fun to be had debugging that kind of problem.

I've been hacking around with the criteria attempting various permutations such as:
    // DOES NOT WORK
List authors = Author.withCriteria {
'in'('books.name', 'Spook Country')
}

and
    // ALSO DOES NOT WORK
List authors = Author.withCriteria {
'in'('books') {
eq('name', 'Spook Country')
}
}

But I've got nowhere yet. The best I've managed to do is:
    List authors = Author.withCriteria {
books {
eq('name', 'Spook Country')
}
}
// if you have a weak stomach look away now
authors*.refresh()

If you're wondering, yes that is going to execute n+1 selects.

Another option is to give up and use an HQL query.

This problem applies equally to one-to-many (and presumably many-to-many) associations using Set, List or Map.

Wednesday, 2 July 2008

Good Unit Testing Practice

A common but misguided practice that is often seen in unit test designs, is to test some kind of a false moniker on an object, rather than the actual properties of the object that should be tested.
For example. We have a simple "Dog" object and we want to get a list of all Dogs of a certain breed,ordered by the date of birth of the dog.



class Dog{
Breed breed;
Date dob;
String name;

}


When testing using this moniker style we might test like this (notice that i have introduced errors but the tests still pass since they are testing the moniker and not the properties that should be tested)...


void testGetAllAlsationsOrderedByAgeUsingMonikers(){

Dog fido = new Dog(name:'alsation born last year', dob: '01/01/2006', breed:Breed.ALSATION)
Dog woofer = new Dog(name:'alsation born 2 years ago', dob:'01/01/2008',breed:Breed.LABRADOR)
Dog mutley = new Dog(name:'alsation born this year', dob: '01/01/2008', breed:Breed.ALSATION)

List dogs = DogService.getDogByBreed(Breed.ALSATION)
assertTrue 'alsation born 2 years ago', dogs[0].name
assertTrue 'alsation born born last year', dogs[1].name
assertTrue 'alsation born this year', dogs[2].name

}


the problem with this is that we are actually not testing for the real properties that we should be testing for, and we are relying on the fact that we didn't introduce any errors in our test set up. which, incidentally,we did, just to prove the point!


A much better way to test this would be the following...



void testGetAllAlsationsOrderedByAgeCorrectly(){

Dog fido = new Dog(name:'not relevant', dob: '01/01/2006', breed:Breed.ALSATION)
Dog woofer = new Dog(name:'not relevant', dob: '01/01/2008', breed:Breed.LABRADOR)
Dog mutley = new Dog(name:'not relevant', dob: '01/01/2008', breed:Breed.ALSATION)

List dogs = DogService.getDogByBreed(Breed.ALSATION)
assertEquals(2, dogs.size())
assertTrue( isOrderedByDateOfBirth( dogs) )
assertTrue ( dogs.every{ Dog dog -> dog.breed=Breed.ALSATION} )

}

private boolean isOrderedByDateOfBirth( List dogs) {

def dobComparator = [compare:{Dog dog1, Dog dog2 ->
dog1.dob == dob2.dob ? 0 :dog1.dob > dog2.dob? 1 : -1 }] as Comparator
return dogs.sort(dobComparator) == dogs


}