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.