为什么在启用了Spring Security的情况下,MockMvc在有效路径上返回404错误?

huangapple go评论68阅读模式
英文:

Why does MockMvc in return 404 error on valid path with Spring Security enabled?

问题

Sure, here is the translated content:

@SpringBootTest
@WebAppConfiguration
class UserControllerImplTest {

    private MockMvc mockMvc;

    @Autowired
    private WebApplicationContext wac;

    @Autowired
    private AuthCookieFilter authCookieFilter;

    @MockBean
    private UserDTOService userDTOService;

    @MockBean
    private ImageDTOService imageDTOService;

    @BeforeEach
    public void setUp() {
        mockMvc = MockMvcBuilders.webAppContextSetup(wac)
                .addFilter(authCookieFilter).build();
    }

    @Test
    public void whenUsers_shouldReturnUsers() throws Exception {

        UserDTO user1 = getSampleUser("user1");
        UserDTO user2 = getSampleUser("user2");
        UserDTO user3 = getSampleUser("user3");
        List<UserDTO> users = List.of(user1, user2, user3);

        Mockito.when(userDTOService.getAll()).thenReturn(users);

        mockMvc.perform(get("/user"))
                .andExpect(status().isOk())
                .andExpect(content().string(users.toString()));
    }

    private UserDTO getSampleUser(String username) {
        return UserDTO.builder()
                .username(username)
                .email(username + "@example.com")
                .password("password")
                .registerTime(LocalDateTime.now())
                .isActive(true)
                .build();
    }
}

Please note that the code has been translated as per your request, but I'm not sure whether the code itself is free of issues or will execute successfully. If you encounter any errors or issues, feel free to ask for assistance.

英文:

I'm trying to test my Spring Boot REST Controller with MockMvc. Following is my test class:

UserControllerImplTest (version 1)

@SpringBootTest
@AutoConfigureMockMvc
class UserControllerImplTest {
@Autowired
private MockMvc mockMvc;
@MockBean
private UserDTOService userDTOService;
@MockBean
private ImageDTOService imageDTOService;
@Test
public void whenUsers_shouldReturnUsers() throws Exception {
UserDTO user1 = getSampleUser(&quot;user1&quot;);
UserDTO user2 = getSampleUser(&quot;user2&quot;);
UserDTO user3 = getSampleUser(&quot;user3&quot;);
List&lt;UserDTO&gt; users = List.of(user1, user2, user3);
Mockito.when(userDTOService.getAll()).thenReturn(users);
mockMvc.perform(get(&quot;/user&quot;))
.andExpect(status().isOk())
.andExpect(content().string(users.toString()));
}
private UserDTO getSampleUser(String username) {
return UserDTO.builder()
.username(username)
.email(username + &quot;@example.com&quot;)
.password(&quot;password&quot;)
.registerTime(LocalDateTime.now())
.isActive(true)
.build();
}
}

Controller to be tested:
UserController:

@Api(tags={&quot;User info&quot;})
@RequestMapping(&quot;/user&quot;)
public interface UserController {
@ApiOperation(value = &quot;Returns list of users&quot;)
@GetMapping(&quot;/&quot;)
@PreAuthorize(&quot;hasRole(&#39;ROLE_ADMIN&#39;)&quot;)
ResponseEntity&lt;List&lt;UserDTO&gt;&gt; users();
@ApiOperation(value = &quot;Returns single user&quot;)
@GetMapping(&quot;/{username}&quot;)
ResponseEntity&lt;UserDTO&gt; userInfo(@PathVariable String username);
@ApiOperation(value = &quot;Returns list of images uploaded by user authenticated with session cookie&quot;)
@GetMapping(&quot;/images&quot;)
@PreAuthorize(&quot;isFullyAuthenticated()&quot;)
ResponseEntity&lt;List&lt;ImageDTO&gt;&gt; userImages(@AuthenticationPrincipal CustomUserDetails userDetails
);
}

its implementation:
UserControllerImpl

@Service
@RequiredArgsConstructor
public class UserControllerImpl implements UserController {
private final UserDTOService userDTOService;
private final ImageDTOService imageDTOService;
@Override
public ResponseEntity&lt;List&lt;UserDTO&gt;&gt; users() {
return ResponseEntity.status(HttpStatus.OK).body(List.of(UserDTO.builder().username(&quot;aaa&quot;).build()));
//        Optional&lt;List&lt;UserDTO&gt;&gt; users = Optional.ofNullable(userDTOService.getAll());
//        if (users.isEmpty()) {
//            return ResponseEntity.status(HttpStatus.NOT_FOUND).build();
//        }
//
//        return ResponseEntity.status(HttpStatus.OK).body(users.get());
}
@Override
public ResponseEntity&lt;UserDTO&gt; userInfo(String username) {
Optional&lt;UserDTO&gt; requestedUser = Optional.ofNullable(userDTOService.findByUsername(username));
if (requestedUser.isEmpty()) {
return ResponseEntity.status(HttpStatus.NOT_FOUND).build();
}
return ResponseEntity.status(HttpStatus.OK).body(requestedUser.get());
}
@Override
public ResponseEntity&lt;List&lt;ImageDTO&gt;&gt; userImages(@AuthenticationPrincipal CustomUserDetails userDetails) {
UserDTO user = userDetails.getUser();
Optional&lt;List&lt;ImageDTO&gt;&gt; images = Optional.ofNullable(imageDTOService.findAllUploadedBy(user));
if (images.isEmpty()) {
return ResponseEntity.status(HttpStatus.OK).body(Collections.emptyList());
}
return ResponseEntity.status(HttpStatus.OK).body(images.get());
}
}

The method I'm invoked in the controller (users()) was modified, just to be sure it'll always return 200.

The test fails because of:

java.lang.AssertionError: Status expected:&lt;200&gt; but was:&lt;404&gt;
Expected :200
Actual   :404

I had a strong suspicion that the Filter Bean I defined might be a culprit here.

AuthCookieFilter

@RequiredArgsConstructor
@Component
public class AuthCookieFilter extends GenericFilterBean {
private final SessionDTOService sessionDTOService;
private final AppConfig appConfig;
@Override
public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain) throws IOException, ServletException {
Optional&lt;String&gt; sessionId = Optional.ofNullable(extractAuthCookie((HttpServletRequest) servletRequest));
if (sessionId.isPresent()) {
Optional&lt;SessionDTO&gt; sessionDTO = Optional.ofNullable(sessionDTOService.findByIdentifier(sessionId.get()));
if (sessionDTO.isPresent()) {
UserDTO userDTO = sessionDTO.get().getUser();
CustomUserDetails customUserDetails = new CustomUserDetails(userDTO);
SecurityContextHolder.getContext().setAuthentication(new UserAuthentication(customUserDetails));
}
}
HttpServletResponse response = (HttpServletResponse) servletResponse;
response.setHeader(&quot;Access-Control-Allow-Origin&quot;, appConfig.getCorsHosts());
response.setHeader(&quot;Access-Control-Allow-Methods&quot;, &quot;POST, PUT, GET, OPTIONS, DELETE&quot;);
response.setHeader(&quot;Access-Control-Max-Age&quot;, &quot;3600&quot;);
response.setHeader(&quot;Access-Control-Allow-Headers&quot;, &quot;Access-Control-Allow-Origin, Authorization, Content-Type, Cache-Control&quot;);
response.setHeader(&quot;Access-Control-Allow-Credentials&quot;, &quot;true&quot;);
filterChain.doFilter(servletRequest, response);
}
public static String extractAuthCookie(HttpServletRequest request) {
List&lt;Cookie&gt; cookies = Arrays.asList(Optional.ofNullable(request.getCookies()).orElse(new Cookie[0]));
if (!cookies.isEmpty()) {
Optional&lt;Cookie&gt; authCookie = cookies.stream()
.filter(cookie -&gt; cookie.getName().equals(&quot;authentication&quot;))
.findFirst();
if (authCookie.isPresent()) {
return authCookie.get().getValue();
}
}
return null;
}
}

So I decided to configure MockMvc manually, explicitly adding it to the context:
UserControllerImplTest (version 2)

@SpringBootTest
@WebAppConfiguration
class UserControllerImplTest {
private MockMvc mockMvc;
@Autowired
private WebApplicationContext wac;
@Autowired
private AuthCookieFilter authCookieFilter;
@MockBean
private UserDTOService userDTOService;
@MockBean
private ImageDTOService imageDTOService;
@BeforeEach
public void setUp() {
mockMvc = MockMvcBuilders.webAppContextSetup(wac)
.addFilter(authCookieFilter).build();
}
@Test
public void whenUsers_shouldReturnUsers() throws Exception {
UserDTO user1 = getSampleUser(&quot;user1&quot;);
UserDTO user2 = getSampleUser(&quot;user2&quot;);
UserDTO user3 = getSampleUser(&quot;user3&quot;);
List&lt;UserDTO&gt; users = List.of(user1, user2, user3);
Mockito.when(userDTOService.getAll()).thenReturn(users);
mockMvc.perform(get(&quot;/user&quot;))
.andExpect(status().isOk())
.andExpect(content().string(users.toString()));
}
private UserDTO getSampleUser(String username) {
return UserDTO.builder()
.username(username)
.email(username + &quot;@example.com&quot;)
.password(&quot;password&quot;)
.registerTime(LocalDateTime.now())
.isActive(true)
.build();
}
}

But had no success, still getting 404s.

I also considered limiting the test scope to bringing up only the Web layer, by using @WebMvcTest, but my AuthCookieFilter, as you can see above, requires full Spring context to be up.

I noticed, that debugger stops inside doFilter of AuthCookieFilter when I put a breakpoint there, disregarding whether MockMvc is configured automatically or manually. However I cannot reach a breakpoint set inside UserControllerImpl.users().

Web security is enabled in this app and that's were additional filter is being added:
SecurityConfiguration.class

@EnableGlobalMethodSecurity(prePostEnabled = true)
@EnableWebSecurity
@Configuration
@RequiredArgsConstructor
public class SecurityConfiguration extends WebSecurityConfigurerAdapter {
private final CustomUserDetailsService userDetailsService;
private final CustomLogoutSuccessHandler customLogoutSuccessHandler;
private final AuthCookieFilter authCookieFilter;
@Override
protected void configure(AuthenticationManagerBuilder auth) throws Exception {
auth.userDetailsService(userDetailsService)
.passwordEncoder(getPasswordEncoder());
}
@Bean
@Override
protected AuthenticationManager authenticationManager() {
return authentication -&gt; {
throw new AuthenticationServiceException(&quot;Cannot authenticate &quot; + authentication);
};
}
@Override
protected void configure(HttpSecurity http) throws Exception {
http
.sessionManagement(configurer -&gt; configurer.sessionCreationPolicy(SessionCreationPolicy.STATELESS))
.csrf().disable()
.logout(configurer -&gt; {
configurer.addLogoutHandler(new HeaderWriterLogoutHandler(
new ClearSiteDataHeaderWriter(ClearSiteDataHeaderWriter.Directive.ALL)
));
configurer.logoutSuccessHandler(customLogoutSuccessHandler);
configurer.logoutUrl(&quot;/auth/logout&quot;);
configurer.deleteCookies(&quot;authentication&quot;);
})
.exceptionHandling(configurer -&gt; configurer.authenticationEntryPoint(new HttpStatusEntryPoint(HttpStatus.UNAUTHORIZED)))
.addFilterAfter(authCookieFilter, SecurityContextPersistenceFilter.class)
.authorizeRequests()
.antMatchers(&quot;/auth/*&quot;).permitAll()
.and()
.formLogin().permitAll()
;
}
@Bean
public PasswordEncoder getPasswordEncoder() {
String defaultEncodingId = &quot;argon2&quot;;
Map&lt;String, PasswordEncoder&gt; encoders = new HashMap&lt;&gt;();
encoders.put(defaultEncodingId, new Argon2PasswordEncoder(16, 32, 8, 1 &lt;&lt; 16, 4));
return new DelegatingPasswordEncoder(defaultEncodingId, encoders);
}
}

Here are logs of the application start-up and test execution:

21:40:56.203 [main] DEBUG org.springframework.test.context.BootstrapUtils - Instantiating CacheAwareContextLoaderDelegate from class [org.springframework.test.context.cache.DefaultCacheAwareContextLoaderDelegate]
21:40:56.210 [main] DEBUG org.springframework.test.context.BootstrapUtils - Instantiating BootstrapContext using constructor [public org.springframework.test.context.support.DefaultBootstrapContext(java.lang.Class,org.springframework.test.context.CacheAwareContextLoaderDelegate)]
21:40:56.228 [main] DEBUG org.springframework.test.context.BootstrapUtils - Instantiating TestContextBootstrapper for test class [com.ewsie.allpic.user.controller.impl.UserControllerImplTest] from class [org.springframework.boot.test.context.SpringBootTestContextBootstrapper]
21:40:56.237 [main] INFO org.springframework.boot.test.context.SpringBootTestContextBootstrapper - Neither @ContextConfiguration nor @ContextHierarchy found for test class [com.ewsie.allpic.user.controller.impl.UserControllerImplTest], using SpringBootContextLoader
21:40:56.240 [main] DEBUG org.springframework.test.context.support.AbstractContextLoader - Did not detect default resource location for test class [com.ewsie.allpic.user.controller.impl.UserControllerImplTest]: class path resource [com/ewsie/allpic/user/controller/impl/UserControllerImplTest-context.xml] does not exist
21:40:56.240 [main] DEBUG org.springframework.test.context.support.AbstractContextLoader - Did not detect default resource location for test class [com.ewsie.allpic.user.controller.impl.UserControllerImplTest]: class path resource [com/ewsie/allpic/user/controller/impl/UserControllerImplTestContext.groovy] does not exist
21:40:56.240 [main] INFO org.springframework.test.context.support.AbstractContextLoader - Could not detect default resource locations for test class [com.ewsie.allpic.user.controller.impl.UserControllerImplTest]: no resource found for suffixes {-context.xml, Context.groovy}.
21:40:56.241 [main] INFO org.springframework.test.context.support.AnnotationConfigContextLoaderUtils - Could not detect default configuration classes for test class [com.ewsie.allpic.user.controller.impl.UserControllerImplTest]: UserControllerImplTest does not declare any static, non-private, non-final, nested classes annotated with @Configuration.
21:40:56.276 [main] DEBUG org.springframework.test.context.support.ActiveProfilesUtils - Could not find an &#39;annotation declaring class&#39; for annotation type [org.springframework.test.context.ActiveProfiles] and class [com.ewsie.allpic.user.controller.impl.UserControllerImplTest]
21:40:56.320 [main] DEBUG org.springframework.context.annotation.ClassPathScanningCandidateComponentProvider - Identified candidate component class: file [/home/max/Projects/allpic/allpic-backend/target/classes/com/ewsie/allpic/AllpicApplication.class]
21:40:56.321 [main] INFO org.springframework.boot.test.context.SpringBootTestContextBootstrapper - Found @SpringBootConfiguration com.ewsie.allpic.AllpicApplication for test class com.ewsie.allpic.user.controller.impl.UserControllerImplTest
21:40:56.382 [main] DEBUG org.springframework.boot.test.context.SpringBootTestContextBootstrapper - @TestExecutionListeners is not present for class [com.ewsie.allpic.user.controller.impl.UserControllerImplTest]: using defaults.
21:40:56.382 [main] INFO org.springframework.boot.test.context.SpringBootTestContextBootstrapper - Loaded default TestExecutionListener class names from location [META-INF/spring.factories]: [org.springframework.boot.test.mock.mockito.MockitoTestExecutionListener, org.springframework.boot.test.mock.mockito.ResetMocksTestExecutionListener, org.springframework.boot.test.autoconfigure.restdocs.RestDocsTestExecutionListener, org.springframework.boot.test.autoconfigure.web.client.MockRestServiceServerResetTestExecutionListener, org.springframework.boot.test.autoconfigure.web.servlet.MockMvcPrintOnlyOnFailureTestExecutionListener, org.springframework.boot.test.autoconfigure.web.servlet.WebDriverTestExecutionListener, org.springframework.test.context.web.ServletTestExecutionListener, org.springframework.test.context.support.DirtiesContextBeforeModesTestExecutionListener, org.springframework.test.context.support.DependencyInjectionTestExecutionListener, org.springframework.test.context.support.DirtiesContextTestExecutionListener, org.springframework.test.context.transaction.TransactionalTestExecutionListener, org.springframework.test.context.jdbc.SqlScriptsTestExecutionListener, org.springframework.test.context.event.EventPublishingTestExecutionListener, org.springframework.security.test.context.support.WithSecurityContextTestExecutionListener, org.springframework.security.test.context.support.ReactorContextTestExecutionListener]
21:40:56.391 [main] INFO org.springframework.boot.test.context.SpringBootTestContextBootstrapper - Using TestExecutionListeners: [org.springframework.test.context.web.ServletTestExecutionListener@7e22550a, org.springframework.test.context.support.DirtiesContextBeforeModesTestExecutionListener@45e37a7e, org.springframework.boot.test.mock.mockito.MockitoTestExecutionListener@62452cc9, org.springframework.boot.test.autoconfigure.SpringBootDependencyInjectionTestExecutionListener@6941827a, org.springframework.test.context.support.DirtiesContextTestExecutionListener@5a7005d, org.springframework.test.context.transaction.TransactionalTestExecutionListener@5bc9ba1d, org.springframework.test.context.jdbc.SqlScriptsTestExecutionListener@1021f6c9, org.springframework.test.context.event.EventPublishingTestExecutionListener@7516e4e5, org.springframework.security.test.context.support.WithSecurityContextTestExecutionListener@488eb7f2, org.springframework.security.test.context.support.ReactorContextTestExecutionListener@5e81e5ac, org.springframework.boot.test.mock.mockito.ResetMocksTestExecutionListener@4189d70b, org.springframework.boot.test.autoconfigure.restdocs.RestDocsTestExecutionListener@3fa2213, org.springframework.boot.test.autoconfigure.web.client.MockRestServiceServerResetTestExecutionListener@3e7634b9, org.springframework.boot.test.autoconfigure.web.servlet.MockMvcPrintOnlyOnFailureTestExecutionListener@6f0b0a5e, org.springframework.boot.test.autoconfigure.web.servlet.WebDriverTestExecutionListener@6035b93b]
21:40:56.393 [main] DEBUG org.springframework.test.context.support.AbstractDirtiesContextTestExecutionListener - Before test class: context [DefaultTestContext@2a8d39c4 testClass = UserControllerImplTest, testInstance = [null], testMethod = [null], testException = [null], mergedContextConfiguration = [WebMergedContextConfiguration@25b2cfcb testClass = UserControllerImplTest, locations = &#39;{}&#39;, classes = &#39;{class com.ewsie.allpic.AllpicApplication}&#39;, contextInitializerClasses = &#39;[]&#39;, activeProfiles = &#39;{}&#39;, propertySourceLocations = &#39;{}&#39;, propertySourceProperties = &#39;{org.springframework.boot.test.context.SpringBootTestContextBootstrapper=true}&#39;, contextCustomizers = set[[ImportsContextCustomizer@72758afa key = [org.springframework.boot.test.autoconfigure.web.servlet.MockMvcAutoConfiguration, org.springframework.boot.test.autoconfigure.web.servlet.MockMvcWebClientAutoConfiguration, org.springframework.boot.test.autoconfigure.web.servlet.MockMvcWebDriverAutoConfiguration, org.springframework.boot.autoconfigure.security.servlet.SecurityAutoConfiguration, org.springframework.boot.autoconfigure.security.servlet.UserDetailsServiceAutoConfiguration, org.springframework.boot.test.autoconfigure.web.servlet.MockMvcSecurityConfiguration]], org.springframework.boot.test.context.filter.ExcludeFilterContextCustomizer@30b6ffe0, org.springframework.boot.test.json.DuplicateJsonObjectContextCustomizerFactory$DuplicateJsonObjectContextCustomizer@14f232c4, org.springframework.boot.test.mock.mockito.MockitoContextCustomizer@d324b662, org.springframework.boot.test.web.client.TestRestTemplateContextCustomizer@f627d13, org.springframework.boot.test.autoconfigure.properties.PropertyMappingContextCustomizer@4b3fa0b3, org.springframework.boot.test.autoconfigure.web.servlet.WebDriverContextCustomizerFactory$Customizer@479cbee5], resourceBasePath = &#39;src/main/webapp&#39;, contextLoader = &#39;org.springframework.boot.test.context.SpringBootContextLoader&#39;, parent = [null]], attributes = map[&#39;org.springframework.test.context.web.ServletTestExecutionListener.activateListener&#39; -&gt; true]], class annotated with @DirtiesContext [false] with mode [null].
21:40:56.409 [main] DEBUG org.springframework.test.context.support.TestPropertySourceUtils - Adding inlined properties to environment: {spring.jmx.enabled=false, org.springframework.boot.test.context.SpringBootTestContextBootstrapper=true, server.port=-1}
2020-09-13 21:40:56.599  INFO 52850 --- [           main] c.e.a.u.c.impl.UserControllerImplTest    : Starting UserControllerImplTest on Oyashiro-sama with PID 52850 (started by max in /home/max/Projects/allpic/allpic-backend)
2020-09-13 21:40:56.599  INFO 52850 --- [           main] c.e.a.u.c.impl.UserControllerImplTest    : No active profile set, falling back to default profiles: default
2020-09-13 21:40:57.143  INFO 52850 --- [           main] .s.d.r.c.RepositoryConfigurationDelegate : Bootstrapping Spring Data JPA repositories in DEFAULT mode.
2020-09-13 21:40:57.197  INFO 52850 --- [           main] .s.d.r.c.RepositoryConfigurationDelegate : Finished Spring Data repository scanning in 49ms. Found 5 JPA repository interfaces.
2020-09-13 21:40:57.716  INFO 52850 --- [           main] trationDelegate$BeanPostProcessorChecker : Bean &#39;org.springframework.security.config.annotation.configuration.ObjectPostProcessorConfiguration&#39; of type [org.springframework.security.config.annotation.configuration.ObjectPostProcessorConfiguration] is not eligible for getting processed by all BeanPostProcessors (for example: not eligible for auto-proxying)
2020-09-13 21:40:57.722  INFO 52850 --- [           main] trationDelegate$BeanPostProcessorChecker : Bean &#39;objectPostProcessor&#39; of type [org.springframework.security.config.annotation.configuration.AutowireBeanFactoryObjectPostProcessor] is not eligible for getting processed by all BeanPostProcessors (for example: not eligible for auto-proxying)
2020-09-13 21:40:57.723  INFO 52850 --- [           main] trationDelegate$BeanPostProcessorChecker : Bean &#39;org.springframework.security.access.expression.method.DefaultMethodSecurityExpressionHandler@1de08775&#39; of type [org.springframework.security.access.expression.method.DefaultMethodSecurityExpressionHandler] is not eligible for getting processed by all BeanPostProcessors (for example: not eligible for auto-proxying)
2020-09-13 21:40:57.727  INFO 52850 --- [           main] trationDelegate$BeanPostProcessorChecker : Bean &#39;org.springframework.security.config.annotation.method.configuration.GlobalMethodSecurityConfiguration&#39; of type [org.springframework.security.config.annotation.method.configuration.GlobalMethodSecurityConfiguration] is not eligible for getting processed by all BeanPostProcessors (for example: not eligible for auto-proxying)
2020-09-13 21:40:57.731  INFO 52850 --- [           main] trationDelegate$BeanPostProcessorChecker : Bean &#39;methodSecurityMetadataSource&#39; of type [org.springframework.security.access.method.DelegatingMethodSecurityMetadataSource] is not eligible for getting processed by all BeanPostProcessors (for example: not eligible for auto-proxying)
2020-09-13 21:40:57.872  INFO 52850 --- [           main] o.hibernate.jpa.internal.util.LogHelper  : HHH000204: Processing PersistenceUnitInfo [name: default]
2020-09-13 21:40:57.909  INFO 52850 --- [           main] org.hibernate.Version                    : HHH000412: Hibernate ORM core version 5.4.12.Final
2020-09-13 21:40:57.931  INFO 52850 --- [           main] o.hibernate.annotations.common.Version   : HCANN000001: Hibernate Commons Annotations {5.1.0.Final}
2020-09-13 21:40:57.993  INFO 52850 --- [           main] com.zaxxer.hikari.HikariDataSource       : HikariPool-1 - Starting...
2020-09-13 21:40:58.098  INFO 52850 --- [           main] com.zaxxer.hikari.HikariDataSource       : HikariPool-1 - Start completed.
2020-09-13 21:40:58.108  INFO 52850 --- [           main] org.hibernate.dialect.Dialect            : HHH000400: Using dialect: org.hibernate.dialect.H2Dialect
(sql queries here)
2020-09-13 21:40:58.523  INFO 52850 --- [           main] o.h.e.t.j.p.i.JtaPlatformInitiator       : HHH000490: Using JtaPlatform implementation: [org.hibernate.engine.transaction.jta.platform.internal.NoJtaPlatform]
2020-09-13 21:40:58.527  INFO 52850 --- [           main] j.LocalContainerEntityManagerFactoryBean : Initialized JPA EntityManagerFactory for persistence unit &#39;default&#39;
2020-09-13 21:40:59.025  WARN 52850 --- [           main] JpaBaseConfiguration$JpaWebConfiguration : spring.jpa.open-in-view is enabled by default. Therefore, database queries may be performed during view rendering. Explicitly configure spring.jpa.open-in-view to disable this warning
2020-09-13 21:40:59.661  INFO 52850 --- [           main] o.s.s.web.DefaultSecurityFilterChain     : Creating filter chain: any request, [org.springframework.security.web.context.request.async.WebAsyncManagerIntegrationFilter@647bd553, org.springframework.security.web.context.SecurityContextPersistenceFilter@16d7f503, com.ewsie.allpic.user.security.AuthCookieFilter@a47a011, org.springframework.security.web.header.HeaderWriterFilter@1b9785aa, org.springframework.security.web.authentication.logout.LogoutFilter@2ddb260e, org.springframework.security.web.authentication.UsernamePasswordAuthenticationFilter@4ceac22d, org.springframework.security.web.savedrequest.RequestCacheAwareFilter@1203c259, org.springframework.security.web.servletapi.SecurityContextHolderAwareRequestFilter@15311af2, org.springframework.security.web.authentication.AnonymousAuthenticationFilter@54602d5a, org.springframework.security.web.session.SessionManagementFilter@347074a9, org.springframework.security.web.access.ExceptionTranslationFilter@2eebb07, org.springframework.security.web.access.intercept.FilterSecurityInterceptor@441a2d52]
2020-09-13 21:40:59.901  INFO 52850 --- [           main] o.s.s.concurrent.ThreadPoolTaskExecutor  : Initializing ExecutorService &#39;applicationTaskExecutor&#39;
2020-09-13 21:41:00.036  INFO 52850 --- [           main] o.s.b.a.h2.H2ConsoleAutoConfiguration    : H2 console available at &#39;/h2-console&#39;. Database available at &#39;jdbc:h2:mem:default&#39;
2020-09-13 21:41:00.070  INFO 52850 --- [           main] o.s.s.c.ThreadPoolTaskScheduler          : Initializing ExecutorService &#39;taskScheduler&#39;
2020-09-13 21:41:00.111  INFO 52850 --- [           main] o.s.b.t.m.w.SpringBootMockServletContext : Initializing Spring TestDispatcherServlet &#39;&#39;
2020-09-13 21:41:00.111  INFO 52850 --- [           main] o.s.t.web.servlet.TestDispatcherServlet  : Initializing Servlet &#39;&#39;
2020-09-13 21:41:00.118  INFO 52850 --- [           main] o.s.t.web.servlet.TestDispatcherServlet  : Completed initialization in 7 ms
2020-09-13 21:41:00.428  INFO 52850 --- [           main] c.e.a.u.c.impl.UserControllerImplTest    : Started UserControllerImplTest in 4.013 seconds (JVM running for 4.7)
WARNING: An illegal reflective access operation has occurred
WARNING: Illegal reflective access by org.springframework.cglib.core.ReflectUtils (file:/home/max/.m2/repository/org/springframework/spring-core/5.2.5.RELEASE/spring-core-5.2.5.RELEASE.jar) to method java.lang.ClassLoader.defineClass(java.lang.String,byte[],int,int,java.security.ProtectionDomain)
WARNING: Please consider reporting this to the maintainers of org.springframework.cglib.core.ReflectUtils
WARNING: Use --illegal-access=warn to enable warnings of further illegal reflective access operations
WARNING: All illegal access operations will be denied in a future release
MockHttpServletRequest:
HTTP Method = GET
Request URI = /user
Parameters = {}
Headers = []
Body = null
Session Attrs = {}
Handler:
Type = org.springframework.web.servlet.resource.ResourceHttpRequestHandler
Async:
Async started = false
Async result = null
Resolved Exception:
Type = null
ModelAndView:
View name = null
View = null
Model = null
FlashMap:
Attributes = null
MockHttpServletResponse:
Status = 404
Error message = null
Headers = [Access-Control-Allow-Origin:&quot;http://localhost:4200&quot;, Access-Control-Allow-Methods:&quot;POST, PUT, GET, OPTIONS, DELETE&quot;, Access-Control-Max-Age:&quot;3600&quot;, Access-Control-Allow-Headers:&quot;Access-Control-Allow-Origin, Authorization, Content-Type, Cache-Control&quot;, Access-Control-Allow-Credentials:&quot;true&quot;, Vary:&quot;Origin&quot;, &quot;Access-Control-Request-Method&quot;, &quot;Access-Control-Request-Headers&quot;, X-Content-Type-Options:&quot;nosniff&quot;, X-XSS-Protection:&quot;1; mode=block&quot;, Cache-Control:&quot;no-cache, no-store, max-age=0, must-revalidate&quot;, Pragma:&quot;no-cache&quot;, Expires:&quot;0&quot;, X-Frame-Options:&quot;DENY&quot;]
Content type = null
Body = 
Forwarded URL = null
Redirected URL = null
Cookies = []
java.lang.AssertionError: Status expected:&lt;200&gt; but was:&lt;404&gt;
Expected :200
Actual   :404

How can I make the MockMvc work and mock my controller?

答案1

得分: 3

问题在于 /user 确实无处可寻,因此 404 响应是完全合理的。

在更改后:

mockMvc.perform(get(&quot;/user&quot;))

为:

mockMvc.perform(get(&quot;/user/&quot;))
(注意末尾的 /

我能够收到实际的响应并继续进行测试。

英文:

The problem was that /user was indeed nowhere to be found, so 404 response was completely justified.

After chaniging:

mockMvc.perform(get(&quot;/user&quot;))

to:

mockMvc.perform(get(&quot;/user/&quot;))
(note the trailing /)

I was able to recieve the actual response and carry on with the test.

答案2

得分: 2

在我的情况下,我使用 @Import 手动导入了控制器类,并且它起作用了。似乎控制器 Bean 没有被 @WebMvcTest 导入。请看下面的代码:

@ContextConfiguration(
    classes = [TestConfigs::class]
)
@WebMvcTest
@Import(MyController::class)
class MyControllerTests {

    @Test
    fun testPost() {
        // 测试逻辑放在这里
    }
}

请注意我如何使用 @Import@Import(MyController::class)

如果我移除 @Import,我会得到 404 错误。

希望有所帮助。

英文:

In my case, I imported the controller class manually using @Import and it worked. It seems the controller bean had not been imported by @WebMvcTest. See below code :

@ContextConfiguration(
classes = [TestConfigs::class]
)
@WebMvcTest
@Import(MyController::class)
class MyControllerTests {
@Test
fun testPost() {
//test logic goes here
}
}

Note that how I used @Import : @Import(MyController::class).

If I remove @Import, I would get 404.

Hope it helps

答案3

得分: 1

尝试将控制器中的接口移除。

在我的情况下,只有这个操作会导致404错误。如果我在控制器中添加任何接口定义,测试中就会出现404错误,但在工作模式下我的控制器正常工作。我仍在调查这个问题,我认为测试框架不能正确地使用接口定义注册控制器。

已解决

@Import(AopAutoConfiguration.class)

https://stackoverflow.com/a/56504756/20816770

英文:

Try to remove interface from controller.

In my case only this cause 404 error, if I add any interface definition to my controller, I get 404 error in tests, but in work mode my controller working fine. I still investigating this issue, I think test framework cannot register controller correcly with interface definition.

Resolved:

@Import(AopAutoConfiguration.class)

https://stackoverflow.com/a/56504756/20816770

huangapple
  • 本文由 发表于 2020年9月14日 03:50:37
  • 转载请务必保留本文链接:https://go.coder-hub.com/63874954.html
匿名

发表评论

匿名网友

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定