【发布时间】: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))
}
}
调查结果
- 执行模拟请求时会发生此故障
def result = mockMvc.perform(MockMvcRequestBuilders.get(path)。 - 递归如下。
at org.spockframework.spring.mock.DelegatingInterceptor.intercept(DelegatingInterceptor.java:53)
at org.spockframework.mock.runtime.DynamicProxyMockInterceptorAdapter.invoke(DynamicProxyMockInterceptorAdapter.java:34)
- Spring Boot 版本
2.5.4 - Spock 框架版本
2.0-groovy-3.0 - 测试仅适用于控制器。如您所见,正在模拟服务调用。
支持课程
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 identityProviderService在AuthorizeControllerSpec中再次注入,基本上遮蔽了继承的字段,而不是仅仅使用@SpringBean IdentityProviderService identityProviderService = Stub()的BaseMvcSpecification导致问题.
标签: spring-boot groovy spock