Unable to populate java.util.getTimeZone in Grails 3 for spock tests in a Taglib

205 Views Asked by At

I am stuck somewhere in Grails 3 spock Testing for Taglibs. I want to test a closure of taglib, which looks like:

ATagLib.groovy:

Closure a = {attrs ->
    java.util.TimeZone utc = java.util.TimeZone.getTimeZone(FraudnetConstants.TIMEZONE_UTC)
    if (attrs.somevar) {
      out << "${g.message(code: 'some.code')} ${g.formatDate([date: attrs.date, format: "h:mma", timeZone: utc])}"
    } else if (attrs.freq == SomeConstants.VAL || attrs.freq == SomeConstants.VAL) {
      out << g.formatDate([date: attrs.date, format: "E '@' h:mma", timeZone: utc])
    } else {
      out << "${g.message(code: 'some.other.code')} ${g.formatDate([date: attrs.date, format: "h:mma", timeZone: utc])}"
    }
 }

My ATagLibSpec.groovy looks like:

@TestFor(ATagLib)
class ATagLibSpec {
def setup() {
    java.util.TimeZone.metaClass.'static'.getTimeZone  =  {a -> return null }
  }

void 'testmethod'() {
   expect:
   taglib.a()
 }  
}

The exception i am getting while running testcases is: java.lang.NullPointerException: Cannot invoke method getTimeZone() on null object at org.grails.plugins.web.taglib.FormatTagLib$_closure2.doCall(FormatTagLib.groovy:170) at groovy.lang.Closure.call(Closure.java:414) at org.grails.taglib.TagOutput.captureTagOutput(TagOutput.java:64) at org.grails.taglib.TagLibraryMetaUtils.methodMissingForTagLib(TagLibraryMetaUtils.groovy:138) at org.grails.taglib.NamespacedTagDispatcher.methodMissing(NamespacedTagDispatcher.groovy:59)

Can someone point here, what's wrong with the above way of prepopulating getTimeZone.

2

There are 2 best solutions below

0
On

instead of using metaprogramming, it is best to inject a TimezoneFactoryService into your taglib. Having to metaprogram in a test is in my experience an indication of code smell: your code uses a static method to instantiate an object, instead of dependency injection.

Your code could look like this:

Closure a = {attrs->
java.util.TimeZone utc = timezoneFactoryService.getTimeZone(FraudnetConstants.TIMEZONE_UTC)
}

This will allow you to mock your factory service in your spec in a way more convenient way, by using a regular Spock Mock.

@TestFor(ATagLib)
class ATagLibSpec {
def setup() {
   taglib.timezoneFactoryService=Stub(TimezoneFactoryService) {
      getTimeZone(_) >> null
   }
  }
void 'testmethod'() {
   expect:
   taglib.a()
 }  
}
5
On

If you still want to use metaprogramming, be aware that the signature of the method has to match fully (also the parameter types), so:

java.util.TimeZone.metaClass.'static'.getTimeZone  =  {a -> return null }

java.util.TimeZone.getTimeZone("America/Los_Angeles")

This code will get the timezone for America/Los Angeles, but

java.util.TimeZone.metaClass.'static'.getTimeZone  =  {String a -> return null }

java.util.TimeZone.getTimeZone("America/Los_Angeles")

this one will return null as we have modified the method properly via metaprogramming.

Be also aware that you have to use the injected variable tagLib not taglib.