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

vdzxcuhz  于 2022-11-11  发布在  Spring
关注(0)|答案(2)|浏览(180)

我尝试用MockMvc测试我的Sping Boot REST控制器。下面是我的测试类:

用户控制器实施测试(版本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("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();
    }
}

要测试的控制器:用户控制器:

@Api(tags={"User info"})
@RequestMapping("/user")
public interface UserController {

    @ApiOperation(value = "Returns list of users")
    @GetMapping("/")
    @PreAuthorize("hasRole('ROLE_ADMIN')")
    ResponseEntity<List<UserDTO>> users();

    @ApiOperation(value = "Returns single user")
    @GetMapping("/{username}")
    ResponseEntity<UserDTO> userInfo(@PathVariable String username);

    @ApiOperation(value = "Returns list of images uploaded by user authenticated with session cookie")
    @GetMapping("/images")
    @PreAuthorize("isFullyAuthenticated()")
    ResponseEntity<List<ImageDTO>> userImages(@AuthenticationPrincipal CustomUserDetails userDetails
    );
}

其实施:用户控制器实现

@Service
@RequiredArgsConstructor
public class UserControllerImpl implements UserController {

    private final UserDTOService userDTOService;
    private final ImageDTOService imageDTOService;

    @Override
    public ResponseEntity<List<UserDTO>> users() {
        return ResponseEntity.status(HttpStatus.OK).body(List.of(UserDTO.builder().username("aaa").build()));
//        Optional<List<UserDTO>> 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<UserDTO> userInfo(String username) {
        Optional<UserDTO> 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<List<ImageDTO>> userImages(@AuthenticationPrincipal CustomUserDetails userDetails) {
        UserDTO user = userDetails.getUser();

        Optional<List<ImageDTO>> 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());
    }
}

我在控制器(users())中调用的方法被修改了,只是为了确保它总是返回200。
测试失败的原因:

java.lang.AssertionError: Status expected:<200> but was:<404>
Expected :200
Actual   :404

我强烈怀疑我定义的过滤器Bean可能是这里的罪魁祸首。

验证Cookie筛选器

@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<String> sessionId = Optional.ofNullable(extractAuthCookie((HttpServletRequest) servletRequest));

        if (sessionId.isPresent()) {
            Optional<SessionDTO> 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("Access-Control-Allow-Origin", appConfig.getCorsHosts());
        response.setHeader("Access-Control-Allow-Methods", "POST, PUT, GET, OPTIONS, DELETE");
        response.setHeader("Access-Control-Max-Age", "3600");
        response.setHeader("Access-Control-Allow-Headers", "Access-Control-Allow-Origin, Authorization, Content-Type, Cache-Control");
        response.setHeader("Access-Control-Allow-Credentials", "true");
        filterChain.doFilter(servletRequest, response);
    }

    public static String extractAuthCookie(HttpServletRequest request) {
        List<Cookie> cookies = Arrays.asList(Optional.ofNullable(request.getCookies()).orElse(new Cookie[0]));

        if (!cookies.isEmpty()) {
            Optional<Cookie> authCookie = cookies.stream()
                    .filter(cookie -> cookie.getName().equals("authentication"))
                    .findFirst();

            if (authCookie.isPresent()) {
                return authCookie.get().getValue();
            }
        }

        return null;
    }
}

因此,我决定手动配置MockMvc,并将其显式添加到上下文中:用户控制器实施测试(版本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("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();
    }
}

但没有成功,仍然得到404。
我还考虑过通过使用@WebMvcTest将测试范围限制为只启动Web层,但是正如您在上面看到的,我的AuthCookieFilter需要启动整个Spring上下文。
我注意到,当我在AuthCookieFilterdoFilter内放置断点时,调试器会停止,而不管MockMvc是自动配置还是手动配置。但是我无法到达UserControllerImpl.users()内设置的断点。
此应用程序中已启用Web安全性,因此正在添加其他筛选器:安全配置.类

@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 -> {
            throw new AuthenticationServiceException("Cannot authenticate " + authentication);
        };
    }

    @Override
    protected void configure(HttpSecurity http) throws Exception {
        http
                .sessionManagement(configurer -> configurer.sessionCreationPolicy(SessionCreationPolicy.STATELESS))
                .csrf().disable()
                .logout(configurer -> {
                    configurer.addLogoutHandler(new HeaderWriterLogoutHandler(
                        new ClearSiteDataHeaderWriter(ClearSiteDataHeaderWriter.Directive.ALL)
                    ));
                    configurer.logoutSuccessHandler(customLogoutSuccessHandler);
                    configurer.logoutUrl("/auth/logout");
                    configurer.deleteCookies("authentication");
                })
                .exceptionHandling(configurer -> configurer.authenticationEntryPoint(new HttpStatusEntryPoint(HttpStatus.UNAUTHORIZED)))
                .addFilterAfter(authCookieFilter, SecurityContextPersistenceFilter.class)
                .authorizeRequests()
                .antMatchers("/auth/*").permitAll()
                .and()
                .formLogin().permitAll()
        ;
    }

    @Bean
    public PasswordEncoder getPasswordEncoder() {
        String defaultEncodingId = "argon2";
        Map<String, PasswordEncoder> encoders = new HashMap<>();
        encoders.put(defaultEncodingId, new Argon2PasswordEncoder(16, 32, 8, 1 << 16, 4));
        return new DelegatingPasswordEncoder(defaultEncodingId, encoders);
    }
}

以下是应用程序启动和测试执行的日志:

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 'annotation declaring class' 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 = '{}', classes = '{class com.ewsie.allpic.AllpicApplication}', contextInitializerClasses = '[]', activeProfiles = '{}', propertySourceLocations = '{}', propertySourceProperties = '{org.springframework.boot.test.context.SpringBootTestContextBootstrapper=true}', 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 = 'src/main/webapp', contextLoader = 'org.springframework.boot.test.context.SpringBootContextLoader', parent = [null]], attributes = map['org.springframework.test.context.web.ServletTestExecutionListener.activateListener' -> 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 'org.springframework.security.config.annotation.configuration.ObjectPostProcessorConfiguration' 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 'objectPostProcessor' 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 'org.springframework.security.access.expression.method.DefaultMethodSecurityExpressionHandler@1de08775' 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 'org.springframework.security.config.annotation.method.configuration.GlobalMethodSecurityConfiguration' 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 'methodSecurityMetadataSource' 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 'default'
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 'applicationTaskExecutor'
2020-09-13 21:41:00.036  INFO 52850 --- [           main] o.s.b.a.h2.H2ConsoleAutoConfiguration    : H2 console available at '/h2-console'. Database available at 'jdbc:h2:mem:default'
2020-09-13 21:41:00.070  INFO 52850 --- [           main] o.s.s.c.ThreadPoolTaskScheduler          : Initializing ExecutorService 'taskScheduler'
2020-09-13 21:41:00.111  INFO 52850 --- [           main] o.s.b.t.m.w.SpringBootMockServletContext : Initializing Spring TestDispatcherServlet ''
2020-09-13 21:41:00.111  INFO 52850 --- [           main] o.s.t.web.servlet.TestDispatcherServlet  : Initializing Servlet ''
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:"http://localhost:4200", Access-Control-Allow-Methods:"POST, PUT, GET, OPTIONS, DELETE", Access-Control-Max-Age:"3600", Access-Control-Allow-Headers:"Access-Control-Allow-Origin, Authorization, Content-Type, Cache-Control", Access-Control-Allow-Credentials:"true", Vary:"Origin", "Access-Control-Request-Method", "Access-Control-Request-Headers", X-Content-Type-Options:"nosniff", X-XSS-Protection:"1; mode=block", Cache-Control:"no-cache, no-store, max-age=0, must-revalidate", Pragma:"no-cache", Expires:"0", X-Frame-Options:"DENY"]
     Content type = null
             Body = 
    Forwarded URL = null
   Redirected URL = null
          Cookies = []

java.lang.AssertionError: Status expected:<200> but was:<404>
Expected :200
Actual   :404

我如何使MockMvc工作并模拟我的控制器?

y1aodyip

y1aodyip1#

问题是/user确实找不到,所以404响应是完全有道理的。
更改后:
mockMvc.perform(get("/user"))
至:
mockMvc.perform(get("/user/"))(请注意结尾的/
我能够收到实际的回应,并继续进行测试。

4xy9mtcn

4xy9mtcn2#

在我的例子中,我使用@Import手动导入了控制器类,并且成功了。看起来控制器bean没有被@WebMvcTest导入。请看下面的代码:

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

    @Test
    fun testPost() {
        //test logic goes here
    }
}

请注意,我是如何使用@Import的:@Import(MyController::class) .
如果删除@Import,则会得到404。
希望能有所帮助

相关问题