我正在开发一个具有一定数量控制器的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"));
}
}
但是,当我介绍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());
}
}
然后,我设法解决了这个问题:
@RunWith(SpringRunner.class)
@SpringBootTest
@AutoConfigureMockMvc
public class RootControllerMvcTest {
...
}
在这种情况下,我的测试会加载整个Application上下文。
谢谢。
答案 0 :(得分:1)
之前提出的方法在较新版本的spring中似乎是deprecated。
另一方面,现在可以使用相同的功能作为附加参数。
例如,在我的WebMvcTest
案例中,我从
@RunWith(SpringRunner.class)
@WebMvcTest(ImportController.class)
以强>
@RunWith(SpringRunner.class)
@WebMvcTest(value = ImportController.class, secure = false)
所有工作都很好。
答案 1 :(得分: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
,您可以省略网址的http://host:port
部分。它被描述为here。请注意,如果我们像您一样启用了安全性,那么我们需要通过该安全性,就好像我们是生产中的真正客户端一样,这意味着我们不会采取措施安全但积极参与其中,这与我们在单元测试中做了什么。
我们的单元测试和集成测试有不同的目标。使用上面概述的方法,我们可以独立地专注于这些目标。
答案 2 :(得分:0)
答案1:
添加了src/test/java/resources/application-disabled-security.yml
:
安全性: 基本: 启用:false
在mvc测试中添加了@ActiveProfiles("disabled-security")
注释。