简体   繁体   中英

Grails/Spock Unit Testing

I have a controller class with this code:

List<MultipartFile> files = []
List<String> convertedContents = []
def convertedFiles = [:]

try {
    params.myFile.each {
        if (((MultipartFile) it.value).empty) {
            throw new NoUploadedFileException('Break .each closure due to empty input.')
        }
        files.add((MultipartFile) it.value)
    }
} catch (NoUploadedFileException e) {   
    redirect uri: request.getHeader('referer')
    return
}

convertedContents = converterService.convertToBase64(files)
(code omitted)

I also have a test:

def "sampleTest"() {    
    when: 
        controller.sendFax()
    then: 
        thrown(NoUploadedFileException)
    response.redirectedUrl == 'index.gsp'
}

What I'm trying to test is that my Controller would throw a "NoUploadedFileException" when no file are uploaded and the submit button is clicked.

This is the error:

Running 1 unit test... 1 of 1
| Failure:  sampleTest(com.synacy.HomeControllerSpec)
|  Expected exception com.synacy.NoUploadedFileException, but got  
java.lang.NullPointerException
at org.spockframework.lang.SpecInternals.thrownImpl(SpecInternals.java:79)
at com.synacy.HomeControllerSpec.throws NoUploadedFileException and returns to the  
same page when no file is uploaded(HomeControllerSpec.groovy:36)
Caused by: java.lang.NullPointerException: Cannot invoke method convertToBase64() on   
null object
at com.synacy.HomeController.sendFax(HomeController.groovy:43)
at com.synacy.HomeControllerSpec.sampleTest(HomeControllerSpec.groovy:33)

It seems that it isn't going through the try-catch block, or if it is, the implementation is not working.

You should mock your service so your test should look like:

@TestFor(MultipleFileUploadController)
@Mock([ConverterService])
class MultipleFileUploadControllerSpec extends Specification {
...
}

But your test will not pass because you handle NoUploadedFileException in controller, so it will not be caught anywhere in tests, so you should have fail with

Expected exception com.stackoverflow.NoUploadedFileException, but no exception was thrown

Remove the line thrown(NoUploadedFileException) from test.

For a Unit test, in either setup: or when: you have to create the environment your controller is acting in.

Populate the request with whatever headers/params/data are appropriate -- headers, parameters, payload, whatever. Setup the grails application configuration with whatever is appropriate if needed.

Your NPE says that you aren't hitting the controller code you are trying to test. The NPE is because your controller doesn't have a converterService assigned/injected (it's a Unit test). You haven't set any params for your controller, so you aren't entering your .each block of code.

Perhaps controller.params.myFile = new MultipartFile() // just guessing here

Abbreviated example from one of my tests (I want to test the json response, so I have to format the request appropriately):

@TestFor(DirectoryController)
@Mock([SpringSecurityService,ConfigurationService,ConfigurationDomain,EntryDomain])
class DirectoryControllerSpec extends Specification
    def "test getDirectorySources"() {
        setup:
        // not testing authentication, so just return true
        SpringSecurityUtils.metaClass.static.ifAllGranted = { String role ->
            return true
        }
        // mock the data to be returned from the configurationService call
        def configuration = new ConfigurationDomain(id:2,name:'Mock Config',entries:[])
        def typeEntry = new EntryDomain(key:'ldap01.type',value:'ad')
        configuration.entries << typeEntry
        def nameEntry = new EntryDomain(key:'ldap01.name',value:'LDAP01')
        configuration.entries << nameEntry
        // mock the configurationService
        def mockConfigurationService = mockFor(ConfigurationService, true) // loose mock
        mockConfigurationService.demand.getConfigurationById() { Long id ->
            return configuration
        }
        controller.configurationService = mockConfigurationService.createMock()

        when:
        // setup the request attributes
        request.setContentType('application/json')
        request.method = 'GET'
        request.addHeader('Accept','application/json')
        controller.params.id = "2"
        controller.getDirectorySources()

        then:
        response.getText() == '{"sources":[{"key":"ldap01","name":"LDAP01","type":"ad"}]}'

        cleanup:
        // reset the metaClass
        SpringSecurityUtils.metaClass = null
    }
}

Mock any services used by the controller; you aren't testing your converterService, so that should be mocked, and return a known value so you know what the controller should do in response to the data returned from the service.

In short, in a Unit test, you should control everything not in the immediate controller code.

Try mocking your service above your test class as:

@TestFor(MultipleFileUploadController)

@Mock([ConverterService])

This will solve your issue as spock will mock the ConverterService class for you and you can call its methods. Please keep in mind that each and every domains and services you use in your methods should be mocked in your test. Hope it will help.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM