Search code examples
testinggrailscaseexport-to-excel

Test Case for Export to Excel functionality


I have an export to excel functionality in my application. I am to write a test case for teh same.

Any Inputs??Sample Code??

Heres my action code.

When the User clicks on "Download to excel" on the page, below export action is called. I am to test this functionality.

def export={

    def playerId=session["playerId"]

    //Calls calculateId, runs an sql query and returns a List
    tuneInstanceList = tuneService.calculateId(playerId)

    if(params?.format && params.format != "html"){
        response.contentType =     ConfigurationHolder.config.grails.mime.types[params.format]
        response.setHeader("Content-disposition", "attachment; filename=tune.${params.extension}")


        exportService.export(params.format, response.outputStream, tuneInstanceList,[:], [:])
    }

    [tuneInstanceList: tuneInstanceList]

    session.flush()
    session.clear()
}

Edited:

As per Robberts' answer wrote an integration test as below.

    class BinaryOutputControllerTests extends GroovyTestCase { 
    void testExportToExcel() { 
    def controller = new TuneController() 

    controller.session.playerID = "ABG65" 
    controller.params.format = "xls" 
    controller.params.extension = "xls" 

    def model = controller.export() 

    assert controller.response.status == 200 
    assert controller.response.contentAsByteArray.size() == 167336 
    assert controller.response.getContentType() \ 
        == "application/vnd.ms-excel" 
    assert controller.response.getHeader("Content-disposition") \ 
        == "attachment; filename=tune.${controller.params.extension}" 

    assert model.tuneInstanceList.size() 
    assert controller.session.valueNames.size() == 0 
} 

}

However, I am getting the following error. Thoughts?

TuneController.groovy:169 refers to line in export action viz.

exportService.export(params.format, response.outputStream, tuneInstanceList,[:], [:])

TuneControllerTests.groovy:264 refers to a line in the tests written viz.

def model = tuneController.export()

Below is the error that I am getting.

java.lang.reflect.UndeclaredThrowableException
at de.andreasschmitt.export.ExportService$$EnhancerByCGLIB$$cbf864b.export()
at de.andreasschmitt.export.ExportService$export.call(Unknown Source)
at pride.TuneController$_closure5.doCall(TuneController.groovy:169)
at pride.TuneController$_closure5.doCall(TuneController.groovy)
at pride.TuneControllerTests.testExportToExcel(TuneControllerTests.groovy:264)
Caused by: de.andreasschmitt.export.exporter.ExporterNotFoundException: No exporter     found for type: xls
at de.andreasschmitt.export.exporter.DefaultExporterFactory.createExporter    (DefaultExporterFactory.groovy:56)
at de.andreasschmitt.export.exporter.ExporterFactory$createExporter$0.callCurrent    (Unknown Source)
at de.andreasschmitt.export.exporter.DefaultExporterFactory.createExporter    (DefaultExporterFactory.groovy:23)
at de.andreasschmitt.export.exporter.ExporterFactory$createExporter.call(Unknown     Source)
at de.andreasschmitt.export.ExportService.export(ExportService.groovy:19)
at de.andreasschmitt.export.ExportService$$FastClassByCGLIB$$c1bbbb10.invoke()
at net.sf.cglib.proxy.MethodProxy.invoke(MethodProxy.java:149)
at de.andreasschmitt.export.ExportService$$EnhancerByCGLIB$$cbf864b.export()
at de.andreasschmitt.export.ExportService$export.call(Unknown Source)
at pride.TuneController$_closure5.doCall(TuneController.groovy:169)
at pride.TuneController$_closure5.doCall(TuneController.groovy)
at pride.TuneControllerTests.testExportToExcel(TuneControllerTests.groovy:264)
at _GrailsTest_groovy$_run_closure4.doCall(_GrailsTest_groovy:268)
at _GrailsTest_groovy$_run_closure4.call(_GrailsTest_groovy)
at _GrailsTest_groovy$_run_closure2.doCall(_GrailsTest_groovy:225)
at _GrailsTest_groovy$_run_closure1_closure21.doCall(_GrailsTest_groovy:184)
at _GrailsTest_groovy$_run_closure1.doCall(_GrailsTest_groovy:171)
at TestApp$_run_closure1.doCall(TestApp.groovy:101)
at gant.Gant$_dispatch_closure5.doCall(Gant.groovy:381)
at gant.Gant$_dispatch_closure7.doCall(Gant.groovy:415)
at gant.Gant$_dispatch_closure7.doCall(Gant.groovy)
at gant.Gant.withBuildListeners(Gant.groovy:427)
at gant.Gant.this$2$withBuildListeners(Gant.groovy)
at gant.Gant$this$2$withBuildListeners.callCurrent(Unknown Source)
at gant.Gant.dispatch(Gant.groovy:415)
at gant.Gant.this$2$dispatch(Gant.groovy)
at gant.Gant.invokeMethod(Gant.groovy)
at gant.Gant.executeTargets(Gant.groovy:590)
at gant.Gant.executeTargets(Gant.groovy:589)
Caused by: org.springframework.beans.factory.NoSuchBeanDefinitionException: No     bean     named 'xlsExporter' is defined
at de.andreasschmitt.export.exporter.DefaultExporterFactory.createExporter            (DefaultExporterFactory.groovy:34)

Solution

  • At the technical level, it has to be decided whether to use an integration or a unit test.

    In Grails unit tests, dependency injection (of services, for example) is not available (however, the test class can provide the controller with dependencies, manually). Any other types of instrumentation, including database access, aren't available, too.
    In my personal opinion, it's better to write finer-grained (unit) tests with controllers; however, if you don't want to mock your services, integration tests do as well.

    The following sample code is an integration test (but a unit test is not that different):

    class BinaryOutputControllerTests extends GroovyTestCase {
        void testExportToExcel() {
            def controller = new BinaryOutputController()
    
            controller.session.playerID = "somePlayerID"
            controller.params.format = "xls"
            controller.params.extension = "xls"
    
            def model = controller.export()
    
            assert controller.response.status == 200
            assert controller.response.contentAsByteArray.size() == 167336
            assert controller.response.getContentType() \
                == "application/vnd.ms-excel"
            assert controller.response.getHeader("Content-disposition") \
                == "attachment; filename=tune.${controller.params.extension}"
    
            assert model.tuneInstanceList.size()
            assert controller.session.valueNames.size() == 0
        }
    }
    

    The sample code shows that you can access the GrailsHttpSession and the controller's params object to initialize values.

    Next, you'd call the controllers action method and retrieve its return values.

    In your testing assertions, you can access the controller's MockHttpServletResponse to query for response values. Again, the GrailsHttpSession object is available as well as the model returned by the controller's action method.

    Obviously, you need to identify use cases; however, these are some technical basics. You should also write failing tests using the shouldFail { ... } closure. And look at the links I've provided as they give you hints on additional options.


    EDIT: As per your further inquiry:

    First, let your ExporterNotFoundException extend RuntimeException (not the checked Exception) in order to avoid that UndeclaredThrowableException. Alternatively (not preferred), add the throws ExporterNotFoundException clause to your DefaultExporterFactory.createExporter(..) method definition. - Groovy's all based on RuntimeExceptions, but you still need to explicitely declare checked Exceptions using the throws clause.

    Second, check whether your ExportService is capable of handling the "xls" format (look for the cause of that ExporterNotFoundException). Apparently, it is not.