7
votes

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

3
Please provide the Command class in the question? - Ramsharan

3 Answers

5
votes

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

2
votes

============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()
}
0
votes

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.