4

I have a custom taglib that needs to make calls to a service method, which in turn uses the Wslite plugin to send and receive SOAP messages. The taglib looks something like this.

class myTagLib {
    def myService
    def thisTag = {
        def parametersFromService = myService.method("argument")
        out << render(template:'/myFolder/myView',
        model:parametersFromService)
    }
}

I'm trying to construct a test for this taglib. My test is something like:

void testThisTag() {
    def appliedTagLib = applyTemplate('<thisTag attr="value" />')
    def parametersFromService = myService.method("argument")
    def renderedTemplate = render(template:'/myFolder/myView',
        model:parametersFromService)
    assertEquals appliedTagLib, renderedTemplate
}

I started by writing this as a Unit test. I tried the simple MockFor(MyService) annotation, as well as mocking them in a more elaborate fashion, but myService object was always null when the taglib tried to invoke it.

Given that, I decided it best to simply do this as an integration test; that would get me access to the plugin and service, no problemo.

However, the applyTemplate method only works if your test class extends the GroovyPagesTestCase. If you do that though, the render method doesn't work, at least not in an integration test. I know that applyTemplate and render do work alongside each other in a unit test for a taglib, as I've tested other taglibs this way.

I seem to be between a rock and a hard place. To get access to the service, plugin, and the SOAP service they invoke, I need an integration test. To use the applyTemplate() method, I need to extend the GroovyPagesTestCase, but that breaks the render method. I've tried adding import grails.test.GroovyPagesTestCase to my test class, and then invoking the applyTemplate method off of it, but that results in a missing method exception.

Thoughts?

The Grails version is 2.0.1, but I could upgrade to 2.2.1 if that would help (our site is in transition).

---Addendum----- elias suggested that I could inject an instance of grails.gsp.PageRenderer to get a working render method. I've tried constructing one:

import org.codehaus.groovy.grails.web.pages.GroovyPagesTemplateEngine
import grails.gsp.PageRenderer

...

def pageRenderer

...

void setUp() {
    context = grailsApplication.mainContext
    taglib = context.getBean(myTagLib.class.name)
    pageRenderer = new PageRenderer(new GroovyPagesTemplateEngine(context.getServletContext()))
}

But receive this error:

java.lang.NullPointerException: Cannot invoke method findTemplateByPath() on null object
at grails.gsp.PageRenderer.renderViewToWriter(PageRenderer.groovy:120)
at grails.gsp.PageRenderer.render(PageRenderer.groovy:77)
at MyTagLibIntegrationTests.testThisTag(MyTagLibIntegrationTests.groovy:37)
jonnybot
  • 2,435
  • 1
  • 32
  • 56
  • 1
    You don't need to initialize it in setUp, just use the correct bean name for the instance, `def groovyPageRenderer` and it should do it. – Elias Dorneles Jun 14 '13 at 19:59

3 Answers3

6

I was struggling with something similar some days ago, here is how I solved it:

  1. Keep the test as in integration test (don't need to extend anything)
  2. Grab the instance of the taglib bean with grailsApplication.mainContext.getBean
  3. Test it using a method call

And that's it! :)

class MyTagLibTests {
    def grailsApplication
    def taglib

    @Before
    void setUp(){
        // grab the taglib instance from Spring context
        taglib = grailsApplication.mainContext.getBean(MyTagLib.class.name)
    }

    @Test
    void myTestAboutThisTag() {
        def thisTagArgs = [:] // set up the arguments here
        assert 'shouldResultThis' == taglib.thisTag(thisTagArgs).toString()
    }
}
Elias Dorneles
  • 22,556
  • 11
  • 85
  • 107
  • That is helpful, but manually defining a string to compare against makes me sad like Old Mammoth. I'm going to see if I can find some way to make the render method available so that I can compare against that. – jonnybot Jun 14 '13 at 18:34
  • 1
    Right! I think you can inject an instance of `groovyPageRenderer` in your test, and try to use its render method. – Elias Dorneles Jun 14 '13 at 18:50
  • ...but if I could do that, why not just extend the GroovyPagesTestCase and compare applyTemplate()'s output to render()'s? See my edit on trying to inject the pageRenderer. This may just be a failure on my part to do basic dependency injection correctly. – jonnybot Jun 14 '13 at 19:51
  • Well, I think you can do that, but it will still only work for a taglib that only renders a template. I offered my approach as a more general solution for an integration test for taglibs. – Elias Dorneles Jun 14 '13 at 20:03
  • And it _was_ very helpful, especially the groovyPageRenderer comment, which got me on the right track. Since the goal in this particular question was to compare a taglib's output against a rendered template, I can't quite accept it as the answer, but I'll try to give credit where credit is due. :) – jonnybot Jun 14 '13 at 20:07
1

So, what I ultimately did was to extend the GroovyPagesTestCase in order to get applyTemplate, then inject the groovyPageRenderer as elias suggested. Persnickity note: you must call your injected PageRenderer groovyPageRenderer in order for spring to do all the appropriate wiring for you. So my test class finally looked like:

class myTaglibIntegrationTests extends GroovyPagesTestCase {
    PageRenderer groovyPageRenderer 
    def imageService
    @Test
    void testThisTag() {
        def appliedTagLib = applyTemplate('<thisTag attr="value" />')
        def parametersFromService = myService.method("argument")
        def renderedTemplate = groovyPageRenderer.render(template:'/myFolder/myView',
            model:parametersFromService)
        assertEquals appliedTagLib, renderedTemplate
    }
}
jonnybot
  • 2,435
  • 1
  • 32
  • 56
  • See elias's answer here for another helpful idea of hacking up a test for a taglib that may work better in other scenarios: http://stackoverflow.com/a/17113855/1524502 – jonnybot Jun 17 '13 at 15:39
0

A cleaner solution would use the GroovyPageUnitTestMixin as it already contains a render method.

@grails.test.mixin.TestMixin(grails.test.mixin.GroovyPageUnitTestMixin)
class myTaglibIntegrationTests {

    def myService

    @Test
    void testThisTag() {
        def appliedTagLib = applyTemplate('<thisTag attr="value" />')
        def parametersFromService = myService.method("argument")
        def renderedTemplate = render(template:'/myFolder/myView', 
                                      model:parametersFromService)
        assertEquals appliedTagLib, renderedTemplate
    }
}

A good write-up on some simple taglib testing can be found here: http://mrhaki.blogspot.com/2013/05/grails-goodness-testing-views-and.html

Glenn Filson
  • 384
  • 3
  • 10