4
votes

I'm working on a fresh Grails project and recently noticed the default convention in the Spring Security Core generated User class now auto-encodes the password via a beforeInsert/Update event. That's a nice, clean, DRY way of doing the encode, and also makes it impossible to forget to do so.

However, now when trying to write up some unit tests which make use of said User class, I find I either have to mock out the springSecurityService (due to the encode), or more preferably (and cleanly), I'd just override the beforeInsert/Update closure with one that does nothing. Typically in Groovy one can override a method using the ExpandoMetaClass, ala...

User.metaClass.beforeInsert = { /* do nothing */ }  

...but I'm finding that the original beforeInsert continues to be called upon creating and saving a new User. This in turn causes my unit tests to blow up. It's trivial for me to just work around this and mock out the service, but the above should work. Am I missing something? Is there something different with GORM's event closures that I'm not picking up on?

3
Another thing to note, I have a @Mock([User]) annotation on my test. This could have something to do with the odd behavior, but just a hunch so far. - James
Is the expando method on instance? // it is never too late to ask :) - Ivar
why do you think, that mocking out springSecurityService is not the preferred way doing it? i mean, one of the first things that is on the top of my head when thinking about dependency injection is exactly that: externalize dependecy management and so be able to create stubs / mock for testing purposes. In my opinion, to change the metaClass of User is not more clear in any way. - Mario David
@MarioDavid It's not a question about what is the right way to test it, that's a matter of opinion. It's a question about why overriding the beforeInsert does not work. I just happen to be using the above description to manifest the issue. - James

3 Answers

3
votes

In order to improve performance Grails uses reflection directly with caches method handles for invoking events and not Groovy's meta layer. The reason is that if you are saving hundreds of domain instances it can seriously hurt performance if Grails had to go through Groovy's meta layer for each event.

There are ways around this such as defining your own User class that disables the event based on a system/environment property that your test sets etc. but there is currently no way to override the methods via meta programming.

1
votes

The beforeInsert closure actually is not only a method like toString() or save(), but also it is a pre-defined event supported by Gorm. Override the method will not prevent Gorm from firing the PreInsert event, which leads to the original process.

0
votes

If necessary, you can replace the code in the beforeInsert with a private method and then override the private method