mrfixij
mrfixij

Reputation: 130

Unit Testing using spock mocking or grails mockFor: Null pointer exception

I'm working on unit testing some code written by my boss. He's drawing a blank and I'm a newbie to TDD, so brainstorm with me please.

my file to be tested, EmailAssist is a helper class to a service not shown here. EmailAssist should reference several other services, including sectionService, as shown.

   class EmailAssist {
       def sectionService
       //condensed to relevant items
       List<CommonsMultipartFile> attachments
       Map emailMap =[:]
       User user
       Boolean valid

       public EmailAssist(){
          valid = false
       }

       public EmailAssist(GrailsParameterMap params, User user){
              //irrelevant code here involving snipped items
              this.setSections(params.list('sections'))
              //series of other similar calls which are also delivering an NPE
       }
       //third constructor using third parameter, called in example but functionally 
       //similar to above constructor.

       //definition of errant function
       void setSections(List sections) {
            emailMap.sections = sectionService.getEmailsInSectionList(sections, user)
        }

The section of SectionService that is being called is as follows.

       Set<String> getEmailsInSectionList(List<String> sections, User user) {
            if(sections && user){
                //code to call DB and update list
            }
            else{
            []
            }

My testing is not providing a section, so this should return an empty list, especially since I can't even access the DB in the unit test.

The unit test is as follows. This is using mockFor, because it didn't appear that spock's mock functionality is what I needed.

@TestMixin(GrailsUnitTestMixin)
class EmailAssistSpec extends Specification {
   @Shared
   GrailsParameterMap params
   @Shared
   GrailsMockHttpServletRequest request = new GrailsMockHttpServletRequest()
   @Shared
   User user
   @Shared
   def sectionService

   def setup() {
       user = new User(id: 1, firstName: "1", lastName: "1", username: "1", email: "[email protected]")
       def sectionServiceMock = mockFor(SectionService)
       sectionServiceMock.demand.getEmailsInSectionList() {
            []
       }
       sectionService = sectionServiceMock.createMock()
   }

   def cleanup(){
   }
   void testGetFiles(){
        when:
        //bunch of code to populate request

        params = newGrailsParameterMap([:], request)
        EmailAssist assist = new EmailAssist(params, request, user)
        //Above constructor call generates NPE

The exact NPE is as follows: java.lang.NullPointerException: Cannot invoke method getEmailsInSectionList() on null object at

        emailMap.sections = sectionService.getEmailsInSectionList(sections, user)

Which is the body of my setSections function, for those of you playing along at home. The NPE stack originates in the constructor call in my testing file. I've also tried using spock-style mocking, but the service is still being considered null. The worst part is that the constructor isn't even what this test is supposed to be testing, it's just refusing to pass this along and as a result making the tests impossible to run.

If there's any more details I can provide to clarify things, let me know, thanks!

Edit: I short circuited the setters in the constructor to complete tests, but that leaves some obvious holes in test coverage that I can't figure out how to fix. Maybe my mocking is located in the wrong place? Spock's mocking documentation isn't very handy for complex functions.

Upvotes: 3

Views: 3976

Answers (1)

John
John

Reputation: 11921

You're getting the NPE because sectionService within the EmailAssist is not being created. Your constructor for EmailAssist calls sectionService.getEmailsInSectionList(sections, user) and because sectionService is null, you get a NPE.

Although you create a mock called sectionService in the test setup(), it doesn't get auto wired / injected into the EmailAssist class. You get very limited auto wiring in Grails unit tests - the documentation isn't very clear on what beans actually get created (and I'm relatively new to Grails/Groovy).

You need to inject the sectionService as you create emailAssist, otherwise it's too late to escape the NPE.

What happens if you modify your call to the constructor in your unit test to be:

@TestMixin(GrailsUnitTestMixin)
class EmailAssistSpec extends Specification {
   @Shared
   GrailsParameterMap params
   @Shared
   GrailsMockHttpServletRequest request = new GrailsMockHttpServletRequest()
   @Shared
   User user
   @Shared
   def mockSectionService = Mock(SectionService)

   def setup() {
       user = new User(id: 1, firstName: "1", lastName: "1", username: "1", email: "[email protected]")
   }

   def cleanup(){
   }

   void testGetFiles(){
        given: "an EmailAssist class with an overridden constructor"

        EmailAssist.metaClass.constructor = { ParamsType params, RequestType request, UserType user -> 
           def instance = new EmailAssist(sectionService: mockSectionService) 
           instance // this returns instance as it's the last line in the closure, but you can put "return instance" if you wish
        }            

        // note that I've moved the population of the request to the given section
        //bunch of code to populate request
        params = newGrailsParameterMap([:], request)

        // this is the list of parameters that you expect sectionService will be called with
        def expectedSectionList = ['some', 'list']

        when: "we call the constructor"
        EmailAssist assist = new EmailAssist(params, request, user)

        then: "sectionService is called by the constructor with the expected parameters"
        1 * mockSectionService.getEmailsInSectionList(expectedSectionList, user)
        // replace a parameter with _ if you don't care about testing the parameter

This answer is based on the blog post from Burt Beckwith here.

Upvotes: 1

Related Questions