使用SpringSecurity和Mock进行Spring Boot单元测试

时间:2017-10-27 18:56:42

标签: java spring unit-testing

目前我有几个单元测试工作正常。 在我的单元测试中,在init上,我包含了以下代码:

@Mock
private UsersServices usersServices;

@InjectMocks
private UsersController usersControllers;

@Before
public void init() {
  this.mvc = MockMvcBuilders.standaloneSetup(usuariosController)
                            .addFilter(springSecurityFilterChain)
                            .setControllerAdvice(new UsuariosControllerAdvice(logService)).build();
}

这很有效,但忽略了一些授权注释,如@PreAuthorize。 (在我的WebSecurityConfig中,我已经添加了@EnableGlobalMethodSecurity(prePostEnabled = true)注释。

所以,过了一段时间,我发现了以下代码:

@Mock
private UsersServices usersServices;

@InjectMocks
private UsersController usersControllers;

@Autowired
private WebApplicationContext wac;

@Before
public void init() {
  this.mvc = MockMvcBuilders
      .webAppContextSetup(wac)
      .addFilter(springSecurityFilterChain)
      apply(SecurityMockMvcConfigurers.springSecurity(springSecurityFilterChain))
      .build();
}

现在,授权注释(@PreAuthorize)有效,但UsersServices模拟没有。当我在单元测试中调用我的控制器方法时,调用了真实的UserServices,而不是模拟。

这是一个模拟UserServices

的单元测试
when(usersServices.getUserAvatar(anyString())).thenReturn(
    CompletableFuture.completedFuture(Optional.empty()));

MvcResult result = mvc.perform(
    get("/api/users/avatar/{login:.+}", "mock.user")
        .header("Authorization", testHelpers.buildJwtToken("USER")))
        .andReturn();

    mvc.perform(asyncDispatch(result))
       .andExpect(status().isNotFound());

没有standaloneSetup,就会调用真实的userServices.getUserAvatar

1 个答案:

答案 0 :(得分:1)

之所以发生这种情况,是因为您的WebApplicationContext无法通过模拟UsersController了解您的UsersServices。要解决此问题,您有两种选择:

第一种选择是使用

@MockBean
private UsersServices usersServices;

而不是:

@Mock
private UsersServices usersServices;

这会将模拟bean添加到应用程序上下文中,以便Spring知道它,因此将使用它而不是真实的。

第二个选项是手动将控制器直接设置在WebApplicationContext内。此选项不应该“在家中尝试”,但对于因为旧的春季版本而没有@MockedBean的情况,可以采用解决方法:

AutowireCapableBeanFactory factory = wac.getAutowireCapableBeanFactory();
BeanDefinitionRegistry registry = (BeanDefinitionRegistry) factory;
registry.removeBeanDefinition(beanId);

//create newBeanObj through GenericBeanDefinition

registry.registerBeanDefinition(beanId, newBeanObj);