Spring Boot WebMvc测试不包括Spring Security

问题描述 投票:0回答:3

我正在开发一个具有一定数量控制器的Spring Boot MVC应用程序。我的根控制器是:

@Controller
@RequestMapping("/")
public class RootController {

    @GetMapping
    public String showStartPage() {
        log.info("GET: Show home page");
        return "index";
    }
}

我已经成功地为控制器实现了MVC测试。我的RootController的测试是:

@RunWith(SpringRunner.class)
@WebMvcTest(RootController.class)
public class RootControllerMvcTest {

    @Autowired
    private MockMvc mvc;

    @Test
    public void testRoot() throws Exception {
        mvc.perform(get("/").accept(MediaType.TEXT_HTML))
            .andExpect(status().isOk())
            .andExpect(view().name("index"));
    }
}

Problem:

但是,当我介绍Spring Security身份验证和授权时,所有的mvc控制器测试都崩溃了。根控制器测试的断言错误是:

java.lang.AssertionError: Status 
Expected :200
Actual   :401

我的安全配置是:

@Configuration
@EnableGlobalMethodSecurity(prePostEnabled = true)
@Order(SecurityProperties.ACCESS_OVERRIDE_ORDER)
public class SecurityConfig extends WebSecurityConfigurerAdapter {

    @Autowired
    private UserDetailsService userDetailsService;

    @Override
    protected void configure(HttpSecurity http) throws Exception {
        http
            .authorizeRequests()
            .antMatchers("/", "/fonts/*").permitAll()
            .antMatchers("/user/**").hasAuthority("ADMIN")
            .anyRequest().fullyAuthenticated()
            .and()
            .formLogin()
            .loginPage("/login")
            .failureUrl("/login?error")
            .usernameParameter("email")
            .permitAll()
            .and()
            .logout()
            .logoutUrl("/logout")
            .deleteCookies("remember-me")
            .logoutSuccessUrl("/")
            .permitAll()
            .and()
            .rememberMe();
    }

    @Override
    protected void configure(AuthenticationManagerBuilder auth) throws Exception {
        auth
            .userDetailsService(userDetailsService)
            .passwordEncoder(new BCryptPasswordEncoder());
    }
}

Solution:

然后,我设法用以下方法解决问题:

@RunWith(SpringRunner.class)
@SpringBootTest
@AutoConfigureMockMvc
public class RootControllerMvcTest {
...
}

在这种情况下,我的测试加载整个Application上下文。

My questions are:

  1. 如何将我的mvc测试与认证和授权过程分开并仅测试控制器的逻辑?
  2. 测试身份验证和授权实施的最佳做法是什么?我是否必须将@SpringBootTest用于此目的?
  3. 分别测试我的控制器和安全逻辑是一个很好的决定吗?

谢谢。

java spring-mvc spring-boot spring-security spring-test-mvc
3个回答
1
投票

之前提出的方法似乎是更新版本的春天的deprecated

另一方面,现在可以使用相同的功能作为附加参数。

例如,在我与WebMvcTest的情况下,我搬了

@RunWith(SpringRunner.class)
@WebMvcTest(ImportController.class)

@RunWith(SpringRunner.class)
@WebMvcTest(value = ImportController.class, secure = false)

一切都很好。


0
投票

很多这可能是意见,但我会给我两分钱。首先,目标应该是覆盖面和上市时间。那就是我需要一个合理快速的解决方案来实现并生成可在生产中使用的软件。所以我并不太关心意识形态。

为此,我喜欢使用单元测试和集成测试的混合。在单元测试试图实现接近100%的代码覆盖率的情况下,集成更多地关注与其他系统的交互,例如数据库,客户端,其他服务等。顺便说一句,单元测试和集成测试必须是自动化的,因此集成应该在没有人在场的情况下完成。

我过去使用两种方法对我的控制器进行单元测试。一种是在Spring上下文之外直接调用控制器方法。我们能够达到我们的代码覆盖率目标。这工作了一段时间但是当我们开始使用Spring的HATEOS功能时失败了,这些功能依赖于使用Spring MVC上下文来创建HATEOS所基于的链接。所以我们切换到MockMvc进行单元测试。这很有效,我们实现了覆盖目标。现在,在您的情况下,问题是安全性妨碍了您的单元测试。我会侧面看安全配置,以便我可以达到我的覆盖目标,可以在集成测试中测试安全性。这可以通过在@Profile类上使用SecurityConfig注释来完成。在我们的微服务网络中,我们使用@Profile来启用和禁用配置类。例如,如果使用某个配置文件,我们只会注册到我们的Eureka服务器。有不同的方法可以做到这一点。一种方法是遵循我们为Eureka配置所做的工作,只有在我们称之为active-eureka的配置文件存在时才启用,我们将此命名为。当我们从IDE运行微服务时,这可以防止联系Eureka服务器。在您的情况下,您可以拥有一个名为activate-security的个人资料。这将允许您进行基于MockMvc的单元测试,安全性破坏了乐趣。

现在,通过单元测试,您仍然必须在启用安全性的情况下对您的服务进行集成测试。有不同的方法。我们使用的一种方法是使用@SpringBootTest类和TestRestTemplate类。这启动了我们的微服务,然后我们使用TestRestTemplate对它进行实际服务调用。 TestRestTemplate是一个真正的http RestTemplate客户端,除了它知道您的Web服务正在运行的端口,因此您可以在尝试找出端口时删除一些样板代码。使用TestRestTamplte,您可以省略URL的http://host:port部分。它被描述为here。请注意,如果我们像您一样启用了安全性,那么我们需要通过该安全性,就好像我们是生产中的真正客户端一样,这意味着我们不会采取措施安全但积极参与其中,这与我们在单元测试中做了什么。

我们的单元测试和集成测试有不同的目标。使用上面概述的方法,我们可以独立地专注于这些目标。


0
投票

答案1:


I have excluded Spring Security features from my MVC tests as follows:
  1. 添加了src/test/java/resources/application-disabled-security.yml: security:basic:enabled:false
  2. 在mvc测试中添加了@ActiveProfiles("disabled-security")注释。
© www.soinside.com 2019 - 2024. All rights reserved.