Background
GroovyPageUnitTestMixin
From the Grails guide, testing a view in a unit test involves:
- Annotating the
Specification
with:@TestMixin(GroovyPageUnitTestMixin)
. - Calling
render(view: '...', model: ...)
orrender(template: '...', model: ...)
whose returned value will be aString
of the HTML output.
Custom TagLibs
Normally, without doing anything else, if the view or template uses custom (i.e. not ones from the g:
namespace) TagLibs, they will not be expanded. For example, if a view GSP was like this:
<div id="blah">
<abc:decorate code="${obj}" format="a" />
</div>
the rendered content for the TagLib will be pretty much verbatim of the original GSP. The only exception is that any ${...}
will be evaluated. For example:
<div id="blah">
<abc:decorate code="Object@3234f21" format="a" />
</div>
And from the Grails guide, using mockTagLib(DecorateTagLib)
(assuming the TagLib class for the abc:decorate
tag is implemented in the TagLib class DecorateTagLib
) will then trigger the TagLib:
@TestMixin(GroovyPageUnitTestMixin)
class MyTestSpec extends Specification {
...
void testBlah() {
given:
mockTagLib(DecorateTagLib)
expect:
...
}
}
will now render something like:
<div id="blah">
{output of DecorateTagLib.decorate}
</div>
Note that the same effect can be accomplished using the @Mock()
annotation on the class:
@TestMixin(GroovyPageUnitTestMixin)
@Mock([DecorateTagLib])
class MyTestSpec extends Specification {
...
void testBlah() {
given:
...
expect:
...
}
}
And Now the Quirk…
If there were multiple TagLib classes sharing the same namespace (abc in this example) used in the GSP:
DecorateTagLib.groovy
class DecorateTagLib {
static namespace = 'abc'
def decorate = { attrs -> ... }
}
and FormatTagLib.groovy
class FormatTagLib {
static namespace = 'abc'
def format = { attrs -> ... }
}
and the GSP uses them both:
<div id="blah">
<abc:decorate code="${obj}" format="a" />
<abc:format val="${x}" />
</div>
and only some of them was mocked:
@TestMixin(GroovyPageUnitTestMixin)
@Mock([DecorateTagLib])
// NOTE there is no mocking of the FormatTagLib class implementing abc:format
class MyTestSpec extends Specification {
...
void testBlah() {
given:
...
expect:
...
}
}
then running the test will bring up and error for the TagLibs that are not mocked. For example, the error will be like:Tag [format] does not exist. No tag library found for namespace: abc
To fix this scenario, either:
- Mock all the TagLibs used in the GSP that share the same namespace (e.g. abc in these examples), or
- Redistribute the TagLibs using unique namespaces so that they do not share the same namespace. However, note that the rule stays the same for all the individual namespaces:
- When at least one TagLib from a namespace is mocked, ALL other TagLibs used in the GSP with that namespace must also be mocked.