【问题标题】:Grails how to perform unit test on exceptionsGrails如何对异常执行单元测试
【发布时间】:2019-06-02 20:04:09
【问题描述】:

我正在尝试测试帐户过期异常。

def authfail() {
    String msg = ''
    def exception = session[WebAttributes.AUTHENTICATION_EXCEPTION]

//        println("print exception: ${exception} | ${session} | ${springSecurityService.getCurrentUser()}")
    if (exception) {
        if (exception instanceof AccountExpiredException) {
            msg = message(code: 'springSecurity.errors.login.expired')
        }
        else if (exception instanceof CredentialsExpiredException) {
            msg = message(code: 'springSecurity.errors.login.passwordExpired')
        }
        else if (exception instanceof DisabledException) {
            msg = message(code: 'springSecurity.errors.login.disabled')
        }
        else {
            msg = message(code: 'springSecurity.errors.login.fail')
        }
    }

    if (springSecurityService.isAjax(request)) {
        render([error: msg] as JSON)
    }
    else {
        flash.message = msg
        redirect action: 'auth', params: params
    }
}

我在意识到自己被卡住之前尝试编写上面的测试用例,因为我不知道如何触发过期登录,以便满足抛出 AccountExceptionExpired 异常的单元测试标准。

void "test authFail"() {

when:
    session."${WebAttributes.AUTHENTICATION_EXCEPTION}" = new AccountExpiredException( 'This account has expired' )
    def logexp = controller.authfail()
then:
    logexp == 'springSecurity.errors.login.expired'
when:
    session."${WebAttributes.AUTHENTICATION_EXCEPTION}" = new CredentialsExpiredException( 'This credentials have expired' )
    def passexp = controller.authfail()
then:
    passexp == 'springSecurity.errors.login.passwordExpired'
when:
    session."${WebAttributes.AUTHENTICATION_EXCEPTION}" = new DisabledException( 'The account is disabled' )
    def logdis = controller.authfail()
then:
    logdis == 'springSecurity.errors.login.disabled'
when:
    session."${WebAttributes.AUTHENTICATION_EXCEPTION}" = new UnsupportedOperationException( 'Sorry, we were not able to find a user with that username and password.' )
    def logfail = controller.authfail()
then:
    logfail == 'springSecurity.errors.login.fail'
when:
    controller.authfail()
then:
    1 * springSecurityService.isAjax( _ ) >> true
    controller.response.json == [error :'springSecurity.errors.login.fail']

    }
}

【问题讨论】:

    标签: unit-testing grails spring-security


    【解决方案1】:

    以下内容将测试您的大部分方法:

    import grails.plugin.springsecurity.SpringSecurityService
    import grails.test.mixin.TestFor
    import org.springframework.security.authentication.AccountExpiredException
    import org.springframework.security.authentication.CredentialsExpiredException
    import org.springframework.security.authentication.DisabledException
    import org.springframework.security.web.WebAttributes
    import spock.lang.Specification
    
    @TestFor(YourController)
    class YourControllerSpec extends Specification {
    
    def springSecurityService = Mock( SpringSecurityService )
    
    void setup() {
        controller.springSecurityService = springSecurityService
    }
    
    void "test authFail"() {
        given:
            session."${WebAttributes.AUTHENTICATION_EXCEPTION}" = new AccountExpiredException( 'This account has expired' )
        when:
            controller.authfail()
        then:
            flash.message == 'springSecurity.errors.login.expired'
        when:
            session."${WebAttributes.AUTHENTICATION_EXCEPTION}" = new CredentialsExpiredException( 'This credentials have expired' )
            controller.authfail()
        then:
            flash.message == 'springSecurity.errors.login.passwordExpired'
        when:
            session."${WebAttributes.AUTHENTICATION_EXCEPTION}" = new DisabledException( 'The account is disabled' )
            controller.authfail()
        then:
            flash.message == 'springSecurity.errors.login.disabled'
        when:
            session."${WebAttributes.AUTHENTICATION_EXCEPTION}" = new UnsupportedOperationException( 'Bad stuff' )
            controller.authfail()
        then:
            flash.message == 'springSecurity.errors.login.fail'
        when:
            controller.authfail()
        then:
            1 * springSecurityService.isAjax( _ ) >> true
            response.json == [error :'springSecurity.errors.login.fail']
    }
    }
    

    会话只是一个映射,我们向其中添加字符串常量的键和异常的值。 对于除最后一个测试之外的所有测试,我们将进入最后一个 else 块,在最后一个测试中,我们为 `isAjax' 返回 true。

    【讨论】:

    • 我尝试了您的建议并得到了这个结果。 imgur.com/nuO1cX7
    • 您是否按照建议从authfail 返回msg
    • 我对此非常陌生。我只实现了你的代码并得到了我不太理解的结果,为什么 res NULL 当它已经在上面定义为控制器时。
    • 它应该可以在没有msg 的显式返回的情况下工作,但至少更容易阅读具有显式返回的代码,您是否在测试类中导入了org.springframework.security.web.WebAttributesorg.springframework.security.authentication.AccountExpiredException 类?
    • 是的,我已经导入了这两个,在我导入之前它甚至无法运行
    【解决方案2】:

    虽然这不是 Grails,但它是 SpringBoot 2.0。

    如果 failureHandler 暴露为 bean,则可以窥探它。

    @SpyBean
    AuthenticationFailureHandler failureHandler;
    

    并简单地验证异常已被抛出

    Mockito.verify(failureHandler).onAuthenticationFailure(
        any(),
        any(),
        any(AccountExpiredException.class)
    );
    

    一个简单的test 可以是这样的:

    @Test
    public void accountExpired() throws Exception {
        doReturn(user
            .username("expired")
            .accountExpired(true)
            .build()
        ).when(userDetailsService).loadUserByUsername(any(String.class));
        mvc.perform(
            MockMvcRequestBuilders.post("/login")
                .param("username", "expired")
                .param("password", "password")
        )
            .andExpect(status().is4xxClientError())
            .andExpect(unauthenticated())
        ;
        Mockito.verify(failureHandler).onAuthenticationFailure(
            any(),
            any(),
            any(AccountExpiredException.class)
        );
    }
    

    https://github.com/fhanik/spring-security-community/的所有样本

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 2011-06-06
      • 2019-07-12
      • 2012-10-29
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多