如何在单元测试中模拟 springSecurityService
How to mock springSecurityService in an unit test
我正在对内部创建用户实例的 Grails 控制器方法进行单元测试。用户域 class 使用 Spring 安全插件的 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
中的密码进行编码:
def beforeInsert() {
encodePassword()
}
def beforeUpdate() {
if (isDirty('password')) {
encodePassword()
}
}
protected void encodePassword() {
password = springSecurityService?.passwordEncoder ? springSecurityService.encodePassword(password) : password
}
当springSecurityService
为null时,不调用encodePassword
且不引发NPE
模拟服务的一种方法是使用 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()
的调用将只是 return 纯文本密码。
讨论了使用模拟的方法 here。
当您在 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)
}
}
...
}
我正在对内部创建用户实例的 Grails 控制器方法进行单元测试。用户域 class 使用 Spring 安全插件的 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
中的密码进行编码:
def beforeInsert() {
encodePassword()
}
def beforeUpdate() {
if (isDirty('password')) {
encodePassword()
}
}
protected void encodePassword() {
password = springSecurityService?.passwordEncoder ? springSecurityService.encodePassword(password) : password
}
当springSecurityService
为null时,不调用encodePassword
且不引发NPE
模拟服务的一种方法是使用 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()
的调用将只是 return 纯文本密码。
讨论了使用模拟的方法 here。
当您在 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)
}
}
...
}