我对Spring Security(使用Spring Boot 1.3.1.REASE)和Mockito有一个很大的问题。
当我在SampleService类的方法doIt()
上使用@PreAuthorize
时,Mockito在测试doItTest()
期间不会在SampleService内部嘲笑方法getValue()
(在SampleDao类上声明)。
结果是:java.lang.AssertionError: expected:<false> but was:<true>
它的发生是因为指令
Mockito.when(dao.getValue()).thenReturn(ctrl);
当我在SampleService类的方法doIt()中使用@PreAuthorize时,它不起作用。
当我从方法doIt()
中删除@PreAuthorize
时,测试doItTest()
运行良好(Mockito正在嘲笑getValue()
)。
但是,当我在方法doIt()
上使用@PreAuthorize
运行另一个测试doItDeniedTest()
时,它运行良好,因为@PreAuthorize
抛出AccessDeniedException。
我该怎么修?
谢谢大家。
代码:
@SpringBootApplication
public class AppApplication {
public static void main(String[] args) {
SpringApplication.run(AppApplication.class, args);
}
}
@Configuration
@EnableWebSecurity
@EnableGlobalMethodSecurity(prePostEnabled = true)
public class WebSecurityConfig extends WebSecurityConfigurerAdapter {
@Autowired
private LogoutHandler logoutHandler;
@Autowired
private AuthenticationSuccessHandler authenticationSuccessHandler;
@Autowired
private AccessDeniedHandler accessDeniedHandler;
@Autowired
private AuthenticationFailureHandler authenticationFailureHandler;
@Override
protected void configure(HttpSecurity http) throws Exception {
http
.authorizeRequests()
.antMatchers("/public/**").permitAll()
.antMatchers("/error/**").permitAll()
.and()
.authorizeRequests()
.antMatchers("/").permitAll()
.antMatchers("/adm/**").hasAnyRole(Role.ROOT,Role.ADM)
.anyRequest().authenticated()
.and()
.formLogin()
.loginPage("/login")
.successHandler(authenticationSuccessHandler)
.failureHandler(authenticationFailureHandler)
.defaultSuccessUrl("/home",true)
.permitAll()
.and()
.logout()
.logoutSuccessUrl("/login")
.permitAll()
.addLogoutHandler(logoutHandler)
.and()
.exceptionHandling()
.accessDeniedHandler(accessDeniedHandler);
}
}
@Repository
public class SampleDao implements Dao {
@Override
public boolean getValue(){
return true;
}
}
@Service
public class SampleService implements Service{
@Autowired
private Dao dao;
@Override
@PreAuthorize(value="hasRole('USR')")
public boolean doIt(){
return dao.getValue();
}
}
我的测试课程是:
@RunWith(SpringJUnit4ClassRunner.class)
@SpringApplicationConfiguration(classes = {AppApplication.class})
@WebAppConfiguration
public class SampleServiceImplTest {
@Mock
private SampleDaoImpl dao;
@Autowired
@Qualifier("sampleServiceImpl")
@InjectMocks
private SampleService svc;
@Before
public void initMocks(){
MockitoAnnotations.initMocks(this);
}
@Test(expected=AccessDeniedException.class)
@WithMockUser(username="usr",roles={"ROOT"})
public void doItDeniedTest() {
svc.doIt();
}
@Test
@WithMockUser(username="usr",roles={"USR"})
public void doItTest() {
Boolean ctrl = Boolean.FALSE;
Mockito.when(dao.getValue()).thenReturn(ctrl);
Boolean rt = svc.doIt();
assertEquals(ctrl,rt);
}
}
您正在将集成测试与单元测试相结合。不要那样做。你有两个选择:
- 根本不创建Spring上下文,在测试中显式地连接类,并使用类似的mocking。这将是单元测试
- 如果您想在集成测试中模拟springbean,您需要将其注册到spring上下文中。我写了这篇关于这个主题的博客文章
阅读博客后,我更改了我的测试类。
@RunWith(SpringJUnit4ClassRunner.class)
@SpringApplicationConfiguration(classes = {AppApplication.class,TestConfiguration.class})
@WebAppConfiguration
public class SampleServiceImplTest {
@Configuration
@EnableGlobalMethodSecurity(prePostEnabled = true)
protected static class TestConfiguration {
@Bean
@Primary
public SampleDaoImpl gatSampleDaoImpl(){
return Mockito.mock(SampleDaoImpl.class);
}
}
@Autowired
private SampleDaoImpl dao;
@Autowired
private SampleService svc;
@Before
public void initMocks(){
MockitoAnnotations.initMocks(this);
}
@Test(expected=AccessDeniedException.class)
@WithMockUser(username="usr",roles={"ROOT"})
public void doItDeniedTest() {
svc.doIt();
}
@Test
@WithMockUser(username="usr",roles={"USR"})
public void doItTest() {
Boolean ctrl = Boolean.FALSE;
Mockito.when(dao.getValue()).thenReturn(ctrl);
Boolean rt = svc.doIt();
assertEquals(ctrl,rt);
}
}