Как смоделировать passwordEncoder, используя Спок в модульном тесте Grails - PullRequest
0 голосов
/ 09 апреля 2020

Я мог бы воспользоваться некоторыми советами о том, как смоделировать автоматическую зависимость, используемую в модульном тесте 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

Я попытался заглушить кодировщик пароля и вернул 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)'

Есть ли способ высмеять эту зависимость со Споком?

Ответы [ 2 ]

1 голос
/ 10 апреля 2020

Stub и Mock берут класс - вы даете ему экземпляр с нулевым значением - отсюда и исключение.

Вы должны быть в состоянии посмеяться над этим так:

def mockPasswordEncoder = Mock(PasswordEncoder) 
// note this is the class 
// org.springframework.security.crypto.password.PasswordEncoder
0 голосов
/ 10 апреля 2020

Я попробовал подход Энрихельгесона, и это сработало! Сначала я импортировал PasswordEncoder в тестовый класс

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

, затем реализовал обычную процедуру насмешки. Сначала я был смущен, потому что тестируемый класс просто неявно создал экземпляр класса, определив его.

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

Я также нашел другое решение, не требующее насмешки

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

Оба подхода работают нормально. Спасибо за помощь!

...