grails spock testing failing with 'java.lang.IllegalArgumentException: ServletContext must not be null'
Asked Answered
I

3

7

I having a method in command class, which use messageSource.getMessage(...), as messageSource won't get injected into the commandClass. I use

def messageSource = Holders.applicationContext.getBean("messageSource") inside the commandClass.

My problem is when trying to write unit test this method,

@Before
void setup() {
    Holders.applicationContext.getBean("messageSource")
}

void "testFunction"() {
    //inside testFunction I am using messageSource
    given:
        //required things
    when:
        //call the function
    then:
        //assert
}

after testing this function, I getting the error

java.lang.IllegalArgumentException: ServletContext must not be null at grails.util.Holders.getApplicationContext(Holders.java:80)

Can someone suggest on how to resolve this one.

Update

@Validateable
class commandClass {
    //required fields and constraints
    def formatData(List<commandClass> commandObjs) {
        StringBuilder validationErrors
        commandObjs.each {commandObj->
            validationErrors = new StringBuilder()
            if(commandObj.hasErrors()) {
                commandObj.errors.allErrors.each {it ->
                    validationErrors.append(messageSource.getMessage(it, null)).append('\n')
                }
            }
            commandObj.metaClass.validationErrors = validationErrors
        }

    }
} 

Thanks in Advance

Inseverable answered 23/10, 2014 at 9:40 Comment(1)
Please provide the Command class in the question?Singly
I
5

I found the answer

void setup() {
    mockApplicationContext()
}

def static mockApplicationContext() {
    GrailsUnitTestMixin.initGrailsApplication()
    Holders.grailsApplication = GrailsUnitTestMixin.grailsApplication
    Holders.metaClass.'static'.getApplicationContext = { ->
        return GrailsUnitTestMixin.applicationContext
    }
    Holders.metaClass.applicationContext.getBean = { bean ->
        return GrailsUnitTestMixin.messageSource
    }
}

I 'll update more about the answer later

Inseverable answered 24/10, 2014 at 11:28 Comment(0)
S
2

============UPDATED ANSWER========================

Right now I am using grails 2.4.2.

Here you do not need to use Holders.applicationContext.getBean("messageSource") to get messageSource, it will be automatically injected.

So the example of the Command object:

@Validateable
class Test {
    def messageSource
    String aa

    static constraints = {
    aa blank:false, validator: {val, obj ->
            println obj.messageSource.getMessage("default.paginate.prev",null,LocaleContextHolder.locale)
            ...
        }
    ...

The example of the test :

void "test for valid data"() {
    when:
    def test = new Test(aa:'hello')
    def messageSource = Mock(MessageSource)
    test.messageSource = messageSource
    then:
    test.validate()
}

Instead of Mock, you can also use mockFor.

=========OLD ANSWER============

Instead of directly using messageSource in Command object, you can use service there and wrap the messageSource by the service.

The example of the service :

class I18nMessageService {
    MessageSource messageSource

    def getMessage(String code, Object[] args=null) {
        messageSource.getMessage(code,args,LocaleContextHolder.locale)
    }
}

The example of the Command object :

@Validateable
class Test {
    def i18nMessageService
    String aa

    static constraints = {
        aa blank:false, validator: {val, obj ->
            println obj.i18nMessageService.getMessage("default.paginate.next")
            ...
        }
    }
}

The i18nMessageService is automatically injected to the Test command object while running app.

For test, i18nMessageService should be mocked and injected manually.

The example of the test :

void "test for valid data"() {
    when:
    def test = new Test(aa:'hello')
    def i18nMessageServiceMock = mockFor(I18nMessageService)
    i18nMessageServiceMock.demand.getMessage {'message you wanted'}
    test.i18nMessageService = i18nMessageServiceMock.createMock()
    then:
    test.validate()
}
Singly answered 24/10, 2014 at 6:56 Comment(16)
Thanks for your answer, We can directly use MessageSource, inside the command class only right?Inseverable
Which version of grails do you use?Singly
grails 2.3.8 versionInseverable
Ok thnk you, let me tryInseverable
I am also checking in 2.3.8 version.Singly
What you thinking about this link mkyong.com/spring/…Inseverable
Let us continue this discussion in chat.Inseverable
Hi, with updated code I getting java.lang.NullPointerException: Cannot invoke method getMessage() on null object, means MessageSource is not injectedInseverable
This is not the code obj.i18nMessageService.getMessage("default.paginate.next"), I have to call the service method inside another methodInseverable
I have edited the answer. You need to call obj.messageSource.getMessage to get messageSingly
No Ram it is not working. As I said, I need to call whatever things inside 'formatData' methodInseverable
What's giving you problem?Singly
I think I solved the issue, within sometime I 'll post the codeInseverable
Great. Ok please post it.Singly
Give me sometime. I am working on it, anyway thanks for continous supportInseverable
see my answer, I posted my answerInseverable
S
0

If you are using Grails 3, you just to add the annotation

@TestMixin(GrailsUnitTestMixin)

before your class definition.

This will inject a variable called applicationContext in your unit test.

Salchunas answered 20/12, 2016 at 14:35 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.