5

I am unit testing a Grails controller method that internally creates an user instance. User domain class uses the springSecurityService of the Spring Security plugin to encode the password before inserting it into the database.

Is there a way to mock that springSecurityService from my unit test in order to get rid of that error?

 Failure:  Create new individual member(MemberControllerSpec)
|  java.lang.NullPointerException: Cannot invoke method encodePassword() on null object

Please find my unit test below.

@TestMixin(HibernateTestMixin)
@TestFor(MemberController)
@Domain([User, IndividualPerson])
class MemberControllerSpec extends Specification {

void "Create new individual member"() {

    given:
    UserDetailsService userDetailsService = Mock(UserDetailsService)
    controller.userDetailsService = userDetailsService

    def command = new IndividualPersonCommand()
    command.username = 'scott@tiger.org'
    command.password = 'What ever'
    command.firstname = 'Scott'
    command.lastname = 'Tiger'
    command.dob = new Date()
    command.email = command.username
    command.phone = '89348'
    command.street = 'A Street'
    command.housenumber = '2'
    command.postcode = '8888'
    command.city = 'A City'

    when:
    request.method = 'POST'
    controller.updateIndividualInstance(command)

    then:
    view == 'createInstance'

    and:
    1 * userDetailsService.loadUserByUsername(command.username) >> null

    and:
    IndividualPerson.count() == 1

    and:
    User.count() == 1

    cleanup:
    IndividualPerson.findAll()*.delete()
    User.findAll()*.delete()
}
}
saw303
  • 8,051
  • 7
  • 50
  • 90

3 Answers3

3

One way to mock a service is to use Groovy's MetaClass

import grails.test.mixin.Mock
import grails.plugin.springsecurity.SpringSecurityService

...
@Mock(SpringSecurityService)
class MemberControllerSpec extends Specification {

    def setupSpec() {
        SpringSecurityService.metaClass.encodePassword = { password -> password }
    }

    def cleanupSpec() {
        SpringSecurityService.metaClass = null
    }
....

In this example, the call to SpringSecurityService.encodePassword() will simply return the password in plain text.

An approach using Mocks is discussed here.

Community
  • 1
  • 1
Emmanuel Rosa
  • 9,697
  • 2
  • 14
  • 20
  • This approach does not work for me. My spec tests the controller and not the user class. behind the scene the controller method creates a user instance. And that user instance has no security service injected (since it is a unit test) – saw303 Sep 04 '15 at 07:31
2

You can to use this code to encode password in User:

def beforeInsert() {
    encodePassword()
}

def beforeUpdate() {
    if (isDirty('password')) {
        encodePassword()
    }
}

protected void encodePassword() {
    password = springSecurityService?.passwordEncoder ? springSecurityService.encodePassword(password) : password
}

When springSecurityService is null, encodePassword is not called and NPE is not raised

AA.
  • 4,496
  • 31
  • 35
  • If you're changing your User class code, be mindful that this is changing the class behavior, as opposed to metaClassing SpringSecurityService for the duration of the test, as mentioned by Emmanuel below. – railsdog Sep 03 '15 at 15:43
  • Testing is so wonderful tool that allow to check your code functionality and design/implementation fails. Maybe you only need to review your code instead of to force the test. – AA. Sep 03 '15 at 16:17
0

When you use controller unit test with spring security rest plugin in Grails v4/v3, if your controller method reference springSecurityService methods like 'athenticatedUser', there will be NullPointException, because springSecurityService is not autowired into the spring application context.

Add code like below, you can inject springSecurityService and mock it's methods.

class GuessControllerSpec extends Specification implements ControllerUnitTest<GuessController> {

@Override
Closure doWithSpring() {
    return {
        // mock method
        SpringSecurityService.metaClass.getCurrentUser = {return new User()}
        // inject into spring context
        springSecurityService(SpringSecurityService)
    }
}
...
}
Bob Yang
  • 123
  • 5