Reputation: 525
I created a Before advice using AspectJ:
package test.accesscontrol.permissionchecker;
import test.accesscontrol.database.SessionExpiredException;
import test.database.UsersDatabaseAccessProvider;
import test.common.constants.GlobalConstants;
import test.common.model.AbstractRequest;
import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Before;
import org.springframework.beans.factory.annotation.Autowired;
@Aspect
public class ValidSessionChecker {
private static final int REQUEST_PARAMETER_ARGUMENT_POSITION = GlobalConstants.ZERO;
private UsersDatabaseAccessProvider usersDatabaseAccessProvider;
@Autowired
public ValidSessionChecker(UsersDatabaseAccessProvider usersDatabaseAccessProvider) {
this.usersDatabaseAccessProvider = usersDatabaseAccessProvider;
}
@Before("@annotation(test.accesscontrol.permissionchecker.ValidSessionRequired)")
public void before(JoinPoint joinPoint) throws Throwable {
Object requestParameterObject = joinPoint.getArgs()[REQUEST_PARAMETER_ARGUMENT_POSITION];
AbstractRequest requestParameter = (AbstractRequest) requestParameterObject;
String sessionID = requestParameter.getSessionId();
if(!usersDatabaseAccessProvider.sessionNotExpired(sessionID))
throw new SessionExpiredException(String.format("Session expired: %s", sessionID));
}
}
and a test class:
package test.accesscontrol;
import test.accesscontrol.database.UsersDatabaseAccessProvider;
import test.accesscontrol.permissionchecker.ValidSessionChecker;
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.mockito.InjectMocks;
import org.mockito.Mock;
import org.mockito.MockitoAnnotations;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.http.MediaType;
import org.springframework.test.context.ContextConfiguration;
import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;
import org.springframework.test.context.web.WebAppConfiguration;
import org.springframework.test.web.servlet.MockMvc;
import org.springframework.test.web.servlet.ResultActions;
import org.springframework.test.web.servlet.setup.MockMvcBuilders;
import static org.mockito.Matchers.anyString;
import static org.mockito.Mockito.*;
import static org.mockito.Mockito.never;
import static org.mockito.Mockito.verify;
import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.post;
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status;
@RunWith(SpringJUnit4ClassRunner.class)
@WebAppConfiguration
@ContextConfiguration("file:src/main/webapp/WEB-INF/mvc-dispatcher-servlet.xml")
public class AccessControlControllerTestsWithInjectedMocks {
@Autowired
private org.springframework.web.context.WebApplicationContext wac;
private MockMvc mockMvc;
@Mock
UsersDatabaseAccessProvider usersDatabaseAccessProvider;
@InjectMocks
ValidSessionChecker validSessionChecker;
@InjectMocks
AccessControlController accessControlController;
@Before
public void before() throws Throwable {
//given
MockitoAnnotations.initMocks(this);
when(usersDatabaseAccessProvider.sessionNotExpired("123456")).thenReturn(Boolean.FALSE);
this.mockMvc = MockMvcBuilders.webAppContextSetup(this.wac).build();
}
@Test
public void changePassword_shouldReturnUnauthorizedHttpCodeWhenSessionIsExpired() throws Exception {
//when
ResultActions results = mockMvc.perform(
post("/accesscontrol/changePassword")
.contentType(MediaType.APPLICATION_JSON)
.content("{\"sessionId\":\"123456\", \"oldPassword\":\"password\", \"newPassword\":\"newPassword\"}")
);
//then
results.andExpect(status().isUnauthorized());
verify(usersDatabaseAccessProvider, never()).getSessionOwner(anyString());
verify(usersDatabaseAccessProvider, never()).isCurrentPasswordValid(anyString(), anyString());
verify(usersDatabaseAccessProvider, never()).setNewPassword(anyString(), anyString());
}
}
spring configuration file:
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:context="http://www.springframework.org/schema/context"
xmlns:aop="http://www.springframework.org/schema/aop"
xmlns:mvc="http://www.springframework.org/schema/mvc"
xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans.xsd
http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context.xsd
http://www.springframework.org/schema/mvc http://www.springframework.org/schema/mvc/spring-mvc-3.1.xsd
http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop-3.0.xsd">
<mvc:annotation-driven />
<aop:aspectj-autoproxy />
<bean class="org.springframework.web.servlet.view.InternalResourceViewResolver">
<property name="prefix" value="/WEB-INF/pages/"/>
<property name="suffix" value=".jsp"/>
</bean>
<bean class="org.springframework.context.support.ResourceBundleMessageSource"
id="messageSource">
<property name="basename" value="messages" />
</bean>
<bean id="usersDatabaseAccessProvider" class="test.accesscontrol.database.UsersDatabaseAccessProvider"/>
<bean id="accessControlController" class="test.accesscontrol.AccessControlController">
<property name="sessionExpirationTimeInSeconds" value="600"/>
</bean>
<bean id="validSessionChecker" class="test.accesscontrol.permissionchecker.ValidSessionChecker" />
<bean id="timeDispatcher" class="test.utils.time.TimeDispatcher" scope="singleton" />
</beans>
AccessControlController
@Controller
@RequestMapping("/accesscontrol")
public class AccessControlController {
...
@RequestMapping(value = "changePassword", method = RequestMethod.POST,
consumes = MediaType.APPLICATION_JSON_VALUE)
@ValidSessionRequired
public ResponseEntity<Void> changePassword(@Valid @RequestBody ChangePasswordRequest request) throws OperationForbiddenException {
String sessionId = request.getSessionId();
String userEmailAddress = usersDatabaseAccessProvider.getSessionOwner(sessionId);
String currentPassword = request.getOldPassword();
this.ensureThatCurrentPasswordIsValid(userEmailAddress, currentPassword);
usersDatabaseAccessProvider.setNewPassword(userEmailAddress, request.getNewPassword());
return new ResponseEntity<Void>(HttpStatus.OK);
}
@ExceptionHandler({SessionExpiredException.class})
public ResponseEntity<Void> handleSessionExpiredException(Exception ex) {
return new ResponseEntity<Void>(HttpStatus.UNAUTHORIZED);
}
}
When I call mockMvc.perform(...) it should intercept method, throw an exception and return 401 Unauthorized code.
Of course it doesn't work, I tried to debug the test and:
How can I inject mocked instance of UsersDatabaseAccessProvider into ValidSessionChecker?
Upvotes: 3
Views: 4116
Reputation: 49935
The issue here is that your Mock instances and the ValidSessionChecker
are not Spring beans and so are not being wired into the ValidSessionChecker
managed by Spring. To make the mocks Spring beans instead probably a better approach will be to create another bean definition file which extends the beans defined in the base configuration file and adds mocks:
test-config.xml:
<beans...>
<import resource="base-springmvc-config.xml"/>
<beans:bean name="usersDatabaseAccessProvider" factory-method="mock" class="org.mockito.Mockito">
<beans:constructor-arg value="..UsersDatabaseAccessProvider"></beans:constructor-arg>
</beans:bean>
And then in your test inject behavior into the mock:
public class AccessControlControllerTestsWithInjectedMocks {
@Autowired
private org.springframework.web.context.WebApplicationContext wac;
private MockMvc mockMvc;
@Autowired
UsersDatabaseAccessProvider usersDatabaseAccessProvider;
@Autowired
ValidSessionChecker validSessionChecker;
....
@Before
public void before() throws Throwable {
//given
MockitoAnnotations.initMocks(this);
when(usersDatabaseAccessProvider.sessionNotExpired("123456")).thenReturn(Boolean.FALSE);
this.mockMvc = MockMvcBuilders.webAppContextSetup(this.wac).build();
}
This should cleanly work.
Upvotes: 3