如何在Grails单元测试中使用Spock模拟passwordEncoder



我可以使用一些建议来模拟Grails单元测试中使用的自动连接依赖关系。我省略了大部分不必要的代码,只给出了测试类和测试下文件类中的相关方法

class UserService {
def springSecurityService // spring bean
def passwordEncoder // auto wired as per 
//https://stackoverflow.com/questions/33303585/spring-//security-encode-password-with-       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
}

在没有存根或嘲笑这种依赖关系的情况下,我得到了错误

Cannot invoke method isPasswordValid() on null object

我试图存根密码编码器,并让它返回真正的

def stubPasswordEncoder =  Stub(passwordEncoder) {
isPasswordValid(_, _, _) >> true
}
service.passwordEncoder = stubPasswordEncoder

但这给出了一个错误信息:

Stub in 'spock.mock.MockingApi' cannot be applied to         '(java.lang.Object, groovy.lang.Closure)'

有没有办法嘲笑斯波克的这种依赖关系?

Stub和Mock接受一个类-您给它的实例为null-因此出现异常。

你应该能够这样嘲笑它:

def mockPasswordEncoder = Mock(PasswordEncoder) 
// note this is the class 
// org.springframework.security.crypto.password.PasswordEncoder

我尝试了enrichelgeson的方法,它成功了!我首先将PasswordEncoder导入测试类

import org.springframework.security.crypto.password.PasswordEncoder

然后实现了正常的mocking过程。起初我很困惑,因为他们在测试中的类只是通过定义它隐式地创建了一个类的实例

def stubPasswordEncoder =  Stub(PasswordEncoder) {
isPasswordValid(_, _, _) >> true
}
service.passwordEncoder = stubPasswordEncoder

我还找到了另一个不需要模仿的解决方案

service.passwordEncoder = [ isPasswordValid: { String rawPass, String salt, Null -> true } ]

这两种方法都很有效。谢谢你的帮助!

最新更新