【问题标题】:What is the reason for the Spock recursion?Spock递归的原因是什么?
【发布时间】:2022-01-05 10:34:34
【问题描述】:

我正在使用 Spock Framework 编写控制器测试。一切都被嘲笑,因此没有调用第三方。但是,由于 Spock 框架内的递归,我在运行此测试时遇到如下 Stack Overflow 错误。

org.springframework.web.util.NestedServletException: Handler dispatch failed; nested exception is java.lang.StackOverflowError

    at org.springframework.web.servlet.DispatcherServlet.doDispatch(DispatcherServlet.java:1079)
    at org.springframework.web.servlet.DispatcherServlet.doService(DispatcherServlet.java:963)
    at org.springframework.web.servlet.FrameworkServlet.processRequest(FrameworkServlet.java:1006)
    at org.springframework.web.servlet.FrameworkServlet.doGet(FrameworkServlet.java:898)
    at javax.servlet.http.HttpServlet.service(HttpServlet.java:655)
    at org.springframework.web.servlet.FrameworkServlet.service(FrameworkServlet.java:883)
    at org.springframework.test.web.servlet.TestDispatcherServlet.service(TestDispatcherServlet.java:72)
    at javax.servlet.http.HttpServlet.service(HttpServlet.java:764)
    at org.springframework.mock.web.MockFilterChain$ServletFilterProxy.doFilter(MockFilterChain.java:167)
    at org.springframework.mock.web.MockFilterChain.doFilter(MockFilterChain.java:134)
    at org.springframework.web.filter.RequestContextFilter.doFilterInternal(RequestContextFilter.java:100)
    at org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:119)
    at org.springframework.mock.web.MockFilterChain.doFilter(MockFilterChain.java:134)
    at org.springframework.web.filter.FormContentFilter.doFilterInternal(FormContentFilter.java:93)
    at org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:119)
    at org.springframework.mock.web.MockFilterChain.doFilter(MockFilterChain.java:134)
    at org.springframework.web.filter.CharacterEncodingFilter.doFilterInternal(CharacterEncodingFilter.java:201)
    at org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:119)
    at org.springframework.mock.web.MockFilterChain.doFilter(MockFilterChain.java:134)
    at org.springframework.test.web.servlet.MockMvc.perform(MockMvc.java:183)
    at com.bsgroup.idpservice.idp.controllers.AuthorizeControllerSpec.should obtain authorized user info(AuthorizeControllerSpec.groovy:37)
Caused by: java.lang.StackOverflowError
    at java.base/java.lang.Class.copyMethods(Class.java:3391)
    at java.base/java.lang.Class.getMethods(Class.java:1904)
    at org.spockframework.util.ReflectionUtil.isObjectMethod(ReflectionUtil.java:111)
    at org.spockframework.mock.runtime.DynamicProxyMockInterceptorAdapter.invoke(DynamicProxyMockInterceptorAdapter.java:31)
    at org.spockframework.spring.mock.DelegatingInterceptor.intercept(DelegatingInterceptor.java:53)
    at org.spockframework.mock.runtime.DynamicProxyMockInterceptorAdapter.invoke(DynamicProxyMockInterceptorAdapter.java:34)
    at org.spockframework.spring.mock.DelegatingInterceptor.intercept(DelegatingInterceptor.java:53)
    at org.spockframework.mock.runtime.DynamicProxyMockInterceptorAdapter.invoke(DynamicProxyMockInterceptorAdapter.java:34)
    at org.spockframework.spring.mock.DelegatingInterceptor.intercept(DelegatingInterceptor.java:53)
    at org.spockframework.mock.runtime.DynamicProxyMockInterceptorAdapter.invoke(DynamicProxyMockInterceptorAdapter.java:34)
    at org.spockframework.spring.mock.DelegatingInterceptor.intercept(DelegatingInterceptor.java:53)
    at org.spockframework.mock.runtime.DynamicProxyMockInterceptorAdapter.invoke(DynamicProxyMockInterceptorAdapter.java:34)

我的测试类如下。

package com.abc.idpservice.idp.controllers

import org.springframework.beans.factory.annotation.Autowired
import org.springframework.boot.test.autoconfigure.web.servlet.WebMvcTest
import org.springframework.test.web.servlet.request.MockMvcRequestBuilders
import org.springframework.test.web.servlet.result.MockMvcResultMatchers

import com.abc.idpservice.BaseMvcSpecification
import com.abc.idpservice.common.constants.AuthConstants
import com.abc.idpservice.idp.data.impl.UserAuthorizationResponseImpl
import com.abc.idpservice.idp.services.IdentityProviderService

@WebMvcTest(controllers = AuthorizeController)
class AuthorizeControllerSpec extends BaseMvcSpecification {

    private final DUMMY_USER_ID = '8d826c-4ca6-9c4f-d1a2fa74c965'
    private final DUMMY_BUSINESS_UNIT = 'ABC'
    public final DUMMY_ROLE = 'ROLE'

    @Autowired
    IdentityProviderService identityProviderService;

    def "should obtain authorized user info" () {
        given:
            def path = AuthConstants.Rest.REST_API_PREFIX + 'initLoginFlow'
        and:
            identityProviderService.getAuthorizationInfo(
                    DUMMY_USER_ID, true) >> UserAuthorizationResponseImpl
                    .builder()
                    .businessUnitCode(DUMMY_BUSINESS_UNIT)
                    .commerceUserId(DUMMY_USER_ID)
                    .rememberMeFlag(true)
                    .roles(DUMMY_ROLE)
                    .build()

        when:
            def result = mockMvc.perform(MockMvcRequestBuilders.get(path)
                    .header(AuthConstants.Rest.USER_ID, DUMMY_USER_ID)
                    .header(AuthConstants.Rest.REMEMBER_ME_FLAG, true)
                    .header(AuthConstants.Rest.BUSINESS_UNIT_HEADER_PARAM, DUMMY_BUSINESS_UNIT))

        then:
            result.andExpect(MockMvcResultMatchers.status().isOk())
                    .andExpect(MockMvcResultMatchers.jsonPath('$.Roles').value(DUMMY_ROLE))
                    .andExpect(MockMvcResultMatchers.jsonPath('$.BusinessUnitCode').value(DUMMY_BUSINESS_UNIT))
                    .andExpect(MockMvcResultMatchers.jsonPath('$.CommerceUserID').value(DUMMY_USER_ID))
                    .andExpect(MockMvcResultMatchers.jsonPath('$.rememberMeFlag').value(true))
    }
}

调查结果

  1. 执行模拟请求时会发生此故障def result = mockMvc.perform(MockMvcRequestBuilders.get(path)
  2. 递归如下。
    at org.spockframework.spring.mock.DelegatingInterceptor.intercept(DelegatingInterceptor.java:53)
    at org.spockframework.mock.runtime.DynamicProxyMockInterceptorAdapter.invoke(DynamicProxyMockInterceptorAdapter.java:34)
  1. Spring Boot 版本2.5.4
  2. Spock 框架版本2.0-groovy-3.0
  3. 测试仅适用于控制器。如您所见,正在模拟服务调用。

支持课程

package com.abc.idpservice

import org.spockframework.spring.SpringBean
import org.springframework.beans.factory.annotation.Autowired
import org.springframework.test.web.servlet.MockMvc

import com.abc.idpservice.common.context.SpaRequestContext
import com.abc.idpservice.idp.services.IdentityProviderService

import spock.lang.Specification

class BaseMvcSpecification extends Specification {

    @Autowired
    protected MockMvc mockMvc

    @SpringBean
    protected SpaRequestContext requestContext = Stub()

    protected path(String enpoint) {
        "/api/$enpoint"
    }

    @SpringBean
    protected IdentityProviderService identityProviderService = Stub()
}
@Slf4j
@RestController
@RequestMapping("/api")
@AllArgsConstructor
public class AuthorizeController {

    private final IdentityProviderService identityProviderService;

    @GetMapping("/initLoginFlow")
    public UserAuthorizationResponse initLoginFlow(@RequestHeader("userid") String userId,
                                                   @RequestHeader("remembermeflag") boolean rememberMeFlag) {
        return identityProviderService.getAuthorizationInfo(userId, rememberMeFlag);
    }

}

感谢任何有关原因或解决方案的帮助。

【问题讨论】:

  • 也许 Leonard 可以通过查看您的代码来回答这个问题。我不能。在 GitHub 上有一个 MCVE 会很好。如果你能准备一个,那就太棒了。
  • 鉴于@Nipun 的回答我会假设,通过@Autowired IdentityProviderService identityProviderServiceAuthorizeControllerSpec 中再次注入,基本上遮蔽了继承的字段,而不是仅仅使用@SpringBean IdentityProviderService identityProviderService = Stub()BaseMvcSpecification 导致问题.

标签: spring-boot groovy spock


【解决方案1】:

我的一位同事指出了这个问题。问题原来是在测试类中使用@Autowired。改用@SpringBean 解决了这个问题。

感谢大家的帮助。

干杯

【讨论】:

  • 您能否进一步详细说明,例如,显示出了什么问题以及您是如何解决的?
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2017-04-10
  • 2011-04-13
  • 2020-10-10
  • 1970-01-01
  • 2016-08-28
  • 2016-09-01
  • 2011-12-04
相关资源
最近更新 更多