【问题标题】:How to mock springSecurityService in an unit test如何在单元测试中模拟 springSecurityService
【发布时间】:2015-09-03 11:15:26
【问题描述】:

我正在对内部创建用户实例的 Grails 控制器方法进行单元测试。用户域类使用 Spring Security 插件的springSecurityService 对密码进行编码,然后将其插入数据库。

有没有办法从我的单元测试中模拟 springSecurityService 以消除该错误?

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

请在下面找到我的单元测试。

@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()
}
}

【问题讨论】:

  • User域如何访问springSecurityService?

标签: unit-testing grails grails-2.5


【解决方案1】:

模拟服务的一种方法是使用 Groovy 的 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
    }
....

在本例中,对SpringSecurityService.encodePassword() 的调用将简单地以纯文本形式返回密码。

here 讨论了一种使用 Mocks 的方法。

【讨论】:

  • 这种方法对我不起作用。我的规范测试控制器而不是用户类。在幕后,控制器方法创建了一个用户实例。并且该用户实例没有注入安全服务(因为它是一个单元测试)
【解决方案2】:

您可以使用此代码对User中的密码进行编码:

def beforeInsert() {
    encodePassword()
}

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

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

springSecurityService 为空时,encodePassword 不会被调用并且不会引发 NPE

【讨论】:

  • 如果您要更改 User 类代码,请注意这是在更改类行为,而不是在测试期间对 SpringSecurityService 进行元分类,如下 Emmanuel 所述。
  • 测试是非常棒的工具,可以检查你的代码功能和设计/实现失败。也许你只需要检查你的代码而不是强制测试。
【解决方案3】:

当您在 Grails v4/v3 中使用带有 spring security rest 插件的控制器单元测试时,如果您的控制器方法引用 springSecurityService 方法,如 'athenticatedUser',则会出现 NullPointException,因为 springSecurityService 没有自动装配到 spring 应用程序上下文中。

添加如下代码,你可以注入 springSecurityService 并模拟它的方法。

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)
    }
}
...
}

【讨论】:

    猜你喜欢
    • 2014-06-23
    • 2017-06-14
    • 2013-10-11
    • 1970-01-01
    • 1970-01-01
    • 2019-12-13
    • 2012-07-14
    • 2019-09-21
    • 2013-12-13
    相关资源
    最近更新 更多