I'm trying to test some controller with method-level spring security and I want to mock out the repository dependencies in the controller. Basically I want to test that (a) the methods are enforcing security and (b) other beans invoked in SpEL expressions are working.
My issue is that when using Mockito's @InjectMocks
for instantiating the controller the spring security proxies are not being applied to the controller and the method security is bypassed. If I use @Autowired
to allow Spring to create the controller, my custom method level security logic does get called but the @Mock
objects are not injected.
@RestController
@RequestMapping("/api/projects/{projectId}")
public class ProjectKeywordResource {
//I want to mock this repository
@Inject
private ProjectKeywordRepository projectKeywordRepository;
//Invokes another bean if user not assigned admin role.
@PreAuthorize("hasRole('ROLE_ADMIN')" + " or "
+ "@projectService.canEditProjectData(#projectId)")
@RequestMapping(value = "/projectKeywords", method = RequestMethod.POST, produces = MediaType.APPLICATION_JSON_VALUE)
@Timed
public ResponseEntity<ProjectKeyword> create(
@PathVariable String projectId,
@RequestBody ProjectKeyword projectKeyword)
throws URISyntaxException {
projectKeywordRepository.save(projectKeyword);
return ResponseEntity.created(
new URI("/api/" + projectId + "projectKeywords/"
+ projectKeyword.getId())).body(result);
}
}
My Test Case is here:
@RunWith(SpringJUnit4ClassRunner.class)
@SpringApplicationConfiguration(classes = Application.class)
@WebAppConfiguration
public class ProjectKeywordResourceSecurityTest {
private static final String DEFAULT_PROJECT_ID = "1";
@Mock
private ProjectKeywordRepository projectKeywordRepository;
//@Inject - Adding the following annotation adds the necessary spring security proxies,
but then ProjectKeywordResource uses the real ProjectKeywordRepository not the mock one.
@InjectMocks
private ProjectKeywordResource projectKeywordResource;
@Before
public void setup() {
MockitoAnnotations.initMocks(this);
}
@Test(expected = AccessDeniedException.class)
@WithMockUser
@Transactional
public void testCreateThrowsAccessDenied() throws Exception {
projectKeywordResource.create(DEFAULT_PROJECT_ID, createDefaultProjectKeyword());
}
@Test
@WithMockUser(username = "admin", roles={"ADMIN"})
@Transactional
public void testCreateAuthorizationSuceedsForAdminUser() throws Exception {
projectKeywordResource.create(DEFAULT_PROJECT_ID, createDefaultProjectKeyword());
}
}
Is there a bit of config magic that allows me to wrap the Mockito mock controller with the necessary spring proxies, or alternatively force the use of the Mock on the injected bean in my test case?