如何在 Grails 单元测试中使用 Spock 模拟 passwordEncoder
How to mock passwordEncoder using Spock in a Grails unit test
我可以使用一些关于如何模拟 Grails 单元测试中使用的自动连接依赖项的建议。我已经省略了大部分不必要的代码,只给出了测试 class 和文件 class 下的相关方法 test
class UserService {
def springSecurityService // spring bean
def passwordEncoder // auto wired as per
// bcrypt-algorithm
.....
def passwordPreviouslyUsed(String newPassword, def userId){
def passwordExists = false
def usersPasswords = findPasswordsForUser(userId)
usersPasswords.each{ password ->
if (passwordEncoder.isPasswordValid(oldPassword, newPassword, null)) {
passwordExists = true
}
}
return passwordExists
}
.....
def findPasswordsForUser(def userId){
User foundUser = User.findById(userId)
def passwordsForUser = UserPasswords.createCriteria().list {
eq('user', foundUser)
projections{
property('password')
}
}
passwordsForUser
}
我的测试
class UserServiceSpec extends Specification implements DataTest, ServiceUnitTest<UserService> {
def passwordEncoder
def setupSpec() {
mockDomains User, UserPasswords
}
def setup() {
def stubPasswordEncoder = Stub(passwordEncoder) {
isPasswordValid(_, _, _) >> true
}
service.passwordEncoder = stubPasswordEncoder
}
void "test for user passwordPreviouslyUsed"() {
given: "a user already exists"
setup()
service.createNewUser("testName", "testy@test.com", "Secret1234" )
//^(does some validation, then User.save())
User foundUser = User.findByEmail("testy@test.com")
foundUser.fullName == "testName"
long testUserId = foundUser.id
and: "we update the password for that user, and it to the userPasswords"
UserPasswords newUserPassword = new UserPasswords(
user: foundUser,
password: "newPassword1"
)
newUserPassword.save()
//use passwordPreviouslyUsed method to check a string with the same value as the
//previously
//updated password to check if it has already been used
when: "we check if the password has been used before"
def response = service.passwordPreviouslyUsed("newPassword1", fundsyId)
then:
response == true
}
没有对这个依赖进行 stubbing 或 mocking,我得到了错误
Cannot invoke method isPasswordValid() on null object
我尝试存根密码编码器并得到它 return true
def stubPasswordEncoder = Stub(passwordEncoder) {
isPasswordValid(_, _, _) >> true
}
service.passwordEncoder = stubPasswordEncoder
但这给出了一条错误消息:
Stub in 'spock.mock.MockingApi' cannot be applied to '(java.lang.Object, groovy.lang.Closure)'
有什么方法可以用 Spock 来模拟这种依赖关系吗?
Stub 和 Mock 接受一个 class - 你给它一个空的实例 - 因此是例外。
你应该可以这样模拟它:
def mockPasswordEncoder = Mock(PasswordEncoder)
// note this is the class
// org.springframework.security.crypto.password.PasswordEncoder
我尝试了 enrichelgeson 的方法,它奏效了!
我先导入了PasswordEncoder来测试class
import org.springframework.security.crypto.password.PasswordEncoder
然后执行正常的模拟程序。我最初感到困惑,因为他们 class 在测试中只是通过定义隐式地创建了 class 的实例。
def stubPasswordEncoder = Stub(PasswordEncoder) {
isPasswordValid(_, _, _) >> true
}
service.passwordEncoder = stubPasswordEncoder
我还找到了另一个不需要模拟的解决方案
service.passwordEncoder = [ isPasswordValid: { String rawPass, String salt, Null -> true } ]
两种方法都可以正常工作。感谢您的帮助!
我可以使用一些关于如何模拟 Grails 单元测试中使用的自动连接依赖项的建议。我已经省略了大部分不必要的代码,只给出了测试 class 和文件 class 下的相关方法 test
class UserService {
def springSecurityService // spring bean
def passwordEncoder // auto wired as per
// bcrypt-algorithm
.....
def passwordPreviouslyUsed(String newPassword, def userId){
def passwordExists = false
def usersPasswords = findPasswordsForUser(userId)
usersPasswords.each{ password ->
if (passwordEncoder.isPasswordValid(oldPassword, newPassword, null)) {
passwordExists = true
}
}
return passwordExists
}
.....
def findPasswordsForUser(def userId){
User foundUser = User.findById(userId)
def passwordsForUser = UserPasswords.createCriteria().list {
eq('user', foundUser)
projections{
property('password')
}
}
passwordsForUser
}
我的测试
class UserServiceSpec extends Specification implements DataTest, ServiceUnitTest<UserService> {
def passwordEncoder
def setupSpec() {
mockDomains User, UserPasswords
}
def setup() {
def stubPasswordEncoder = Stub(passwordEncoder) {
isPasswordValid(_, _, _) >> true
}
service.passwordEncoder = stubPasswordEncoder
}
void "test for user passwordPreviouslyUsed"() {
given: "a user already exists"
setup()
service.createNewUser("testName", "testy@test.com", "Secret1234" )
//^(does some validation, then User.save())
User foundUser = User.findByEmail("testy@test.com")
foundUser.fullName == "testName"
long testUserId = foundUser.id
and: "we update the password for that user, and it to the userPasswords"
UserPasswords newUserPassword = new UserPasswords(
user: foundUser,
password: "newPassword1"
)
newUserPassword.save()
//use passwordPreviouslyUsed method to check a string with the same value as the
//previously
//updated password to check if it has already been used
when: "we check if the password has been used before"
def response = service.passwordPreviouslyUsed("newPassword1", fundsyId)
then:
response == true
}
没有对这个依赖进行 stubbing 或 mocking,我得到了错误
Cannot invoke method isPasswordValid() on null object
我尝试存根密码编码器并得到它 return true
def stubPasswordEncoder = Stub(passwordEncoder) {
isPasswordValid(_, _, _) >> true
}
service.passwordEncoder = stubPasswordEncoder
但这给出了一条错误消息:
Stub in 'spock.mock.MockingApi' cannot be applied to '(java.lang.Object, groovy.lang.Closure)'
有什么方法可以用 Spock 来模拟这种依赖关系吗?
Stub 和 Mock 接受一个 class - 你给它一个空的实例 - 因此是例外。
你应该可以这样模拟它:
def mockPasswordEncoder = Mock(PasswordEncoder)
// note this is the class
// org.springframework.security.crypto.password.PasswordEncoder
我尝试了 enrichelgeson 的方法,它奏效了! 我先导入了PasswordEncoder来测试class
import org.springframework.security.crypto.password.PasswordEncoder
然后执行正常的模拟程序。我最初感到困惑,因为他们 class 在测试中只是通过定义隐式地创建了 class 的实例。
def stubPasswordEncoder = Stub(PasswordEncoder) {
isPasswordValid(_, _, _) >> true
}
service.passwordEncoder = stubPasswordEncoder
我还找到了另一个不需要模拟的解决方案
service.passwordEncoder = [ isPasswordValid: { String rawPass, String salt, Null -> true } ]
两种方法都可以正常工作。感谢您的帮助!