spring session is not compatible with MockHttpSession - java

The following test fails when I try to integrate spring session.
class WeChatOAuth2AuthenticationFilterTest extends AbstractWebMvcTest {
#Test
void it_should_redirect_user_to_origin_uri_when_wechat_oauth_is_finished() throws Exception {
String code = "codeToExchangeWeChatUserAccessToken"
String plainUrl = "http://www.example.com/index.html?a=b#/route"
String state = Base64.getUrlEncoder().encodeToString(plainUrl.getBytes("UTF-8"))
WxMpOAuth2AccessToken accessToken = new WeChatUserOAuth2AccessTokenFixture().buildToken()
given(wxMpService.oauth2getAccessToken(code))
.willReturn(accessToken)
this.mockMvc.perform(get("/wechat/oauth/token")
.param("state", state)
.param("code", code))
.andDo(print())
.andExpect(status().is3xxRedirection())
.andExpect(redirectedUrl(plainUrl))
.andExpect(authenticated())
// throws Authentication should not be null
}
}
#Configuration
#EnableSpringHttpSession
public class HttpSessionConfig {
#Bean
protected SessionRepository sessionRepository() {
return new MapSessionRepository();
}
}
After some debugging, I find out that it is probably due to I cannot get HttpSession
// org.springframework.security.web.context.HttpSessionSecurityContextRepository
public SecurityContext loadContext(HttpRequestResponseHolder requestResponseHolder) {
HttpServletRequest request = requestResponseHolder.getRequest();
HttpServletResponse response = requestResponseHolder.getResponse();
HttpSession httpSession = request.getSession(false);
//returns null with spring-session,
//returns a MockHttpSession instance without spring-session
SecurityContext context = readSecurityContextFromSession(httpSession);
Currently, I make the spring session disabled for the tests with #ConditionalProperties. Any better idea is welcome.

This is related to correct setup of you mockMvc object in your test.
For brevity, I assume you can use #SpringBootTest annotation in your project. The codes below shows how you could properly wire in spring-session related classes into your mockMvc.
#RunWith(SpringRunner.class)
#SpringBootTest
#WebAppConfiguration
public class ExampleControllerV2SpringSessionTest {
#Autowired
private WebApplicationContext wac;
#Autowired
private SessionRepository sessionRepository;
#Autowired
private SessionRepositoryFilter sessionRepositoryFilter;
//this is needed to test spring-session specific features
private MockMvc mockMvcWithSpringSession;
#Before
public void setup() throws URISyntaxException {
this.mockMvcWithSpringSession = MockMvcBuilders
.webAppContextSetup(wac)
.addFilter(sessionRepositoryFilter)
.build();
}
//------------------------- BEGIN: Test cases with Spring-session---------------------------------
#Test
public void getANewSpringSession(String requestBody) throws Exception {
MvcResult result = mockMvcWithSpringSession.perform(
get("/v1/sampleendpoint") .header("YOUR_HEADER_NAME", "YOUR_HEADER_VALUE")
.contentType(MediaType.APPLICATION_JSON)
.content("YOUR_SAMPLE_JSON_BODY"))
.andExpect(status().isOk())
.andExpect(header().string("x-auth-token", notNullValue()))
.andReturn();
String xAuthToken = result.getResponse().getHeader(AuthenticationControllerV2.Params.SESSION_KEY);
MapSession curSession = (MapSession) sessionRepository.getSession(xAuthToken);
Assert.assertNotNull(curSession);
}
//------------------------- END: Test cases with Spring-session---------------------------------
}

Related

How to write a unit test for custom Rest template interceptor in spring boot

I have this following custom interceptor in my java spring application as below.
public class AuthInterceptor implements ClientHttpRequestInterceptor {
private final String encodedCredentials;
public AuthInterceptor(String username, String password) {
this(username, password, (Charset) null);
}
public AuthInterceptor(String username, String password, #Nullable Charset charset) {
this.encodedCredentials = HttpHeaders.encodeBasicAuth(username, password, charset);
}
#Override
public ClientHttpResponse intercept(HttpRequest request, byte[] body, ClientHttpRequestExecution execution) throws IOException {
HttpHeaders headers = request.getHeaders();
headers.setBasicAuth(this.encodedCredentials);
return execution.execute(request, body);
}
}
But I am looking to find for some documentation on how to write a unit test for this class and could not find. Any input on how to test would be really helpful
Assuming you only want to test the interception and you already have set up mockito:
#Test
#DisplayName("Should add correct header to authorization")
void encodeProperly() throws Exception {
AuthInterceptor cut = new AuthInterceptor("someuserName","somePaswwordName");
MockHttpServletRequest request = new MockHttpServletRequest("GET", "/test");
cut.intercept(request, null,null,mockedExecution );
ArgumentCaptor<HttpRequest> argumentCapture = ArgumentCaptor.forClass(HttpRequest.class) ;
verify(mockedExecution.execute(argumentCapture.capture(),any()));
assertEquals(expectedEncodedCredentials,argumentCapture.getValue().getHeaders().get(HttpHeaders.AUTHORIZATION));
}
so the steps are:
create your class to test cut
then create a mock of MockHttpServletRequest it will be helpful to perform your check
Unit test that class the same way you unit test any class.
classToTest = new AuthInterceptor()
Call the method with Mock objects.
Some example code:
#ExtendWith(MockitoExtension.class)
public class TestAuthInterceptor
{
private AuthInterceptor classToTest;
#Mock
private ClientHttpRequestExecution mockClientHttpRequestExecution;
#Mock
private ClientHttpResponse mockClientHttpResponse;
#Mock
private HttpHeaders mockHttpHeaders;
#Mock
private HttpRequest mockHttpRequest;
#BeforeEach
public void beforeEach()
{
classToTest = new AuthInterceptor("username", "password");
}
#Test
public void intercept_allGood_success()
{
final ClientHttpResponse actualResult;
final byte[] inputBody = null;
doReturn(mockHttpHeaders).when(mockHttpRequest).getHeaders();
doReturn(mockClientHttpResponse).when(mockClientHttpRequestExecution).execute(mockHttpRequest, inputBody);
actualResult = classToTest.intercept(mockHttpRequest, inputBody, mockClientHttpRequestExecution);
assertSame(mockClientHttpResponse, actualResult);
// other asserts as desired.
}
}

unit testing in spring boot giving error when exception thrown in service

So, I have this unit test that I need to run.
#MockBean
private AppServiceImpl appService;
#Test
public void shouldThrowExceptionWhenAppIdIsNull() throws Exception {
File inputFile = this.getJsonFile();
RequestDto requestDto = objectMapper.readValue(inputFile.getAbsoluteFile(),
RequestDto.class);
AppData appData = requestDto.getAppData();
appData.setAppId(null);
requestDto.setAppData(appData);
when(appService.getUrl(requestDto, "header")).thenThrow(new RequestNotValidException());
String payload = objectMapper.writeValueAsString(requestDto);
this.mockMvc.perform(post(Base_URL + "app/requesturl")
.contentType(contentType).content(payload).header(this.Header, "header"))
.andExpect(status().is4xxClientError());
}
Interface for service:
SO when I run this test, it throws an exception and doesn't actually assert the test here.
I have added #ResponseStatus(HttpStatus.BAD_REQUEST) on top of RequestNotValidException and it extends RunTimeException
And in the second test case, I get empty response. I tried this API vis Postman and I get the response. Everything works fine there.
#Test
public void getCardRegistration() throws Exception {
File inputFile = this.getJsonFile();
RequestDto requestDto = objectMapper.readValue(inputFile.getAbsoluteFile(), RequestDto.class);
ResponseDto responseDto = new ResponseDto();
responseDto.setURL(AuthUtils.randomStringToken(35));
given(appService.getRegistrationUrl(requestDto, "header")).willReturn(responseDto);
String payload = objectMapper.writeValueAsString(requestDto);
MvcResult mvcResult = this.mockMvc.perform(post(Base_URL + "app/requesturl")
.contentType(contentType).content(payload).header(this.Header, "header"))
.andReturn();
String contentAsString = mvcResult.getResponse().getContentAsString();
}
Controller content:
#Autowired
IAppService appService;
#RequestMapping(value = "/app/requesturl", method = RequestMethod.POST)
public ResponseDto getCardsRegistration(#RequestBody #Valid RequestDto requestDto, #RequestHeader(value="X-App-Name", required = true) String header) throws RequestNotValidException, JsonProcessingException {
log.info("Request received in controller: "+ mapper.writeValueAsString(cardRegistrationRequestDto));
log.info("Header value: "+ header);
return this.appService.getRegistrationUrl(requestDto, header);
}
Test Class:
#RunWith(SpringRunner.class)
#SpringBootTest
#AutoConfigureMockMvc
public class AppRestControllerTest {
protected String Base_URL = "/app";
protected String Header = "X-App-Name";
protected MediaType contentType = new MediaType(MediaType.APPLICATION_JSON.getType(),
MediaType.APPLICATION_JSON.getSubtype(),
Charset.forName("utf8"));
#Autowired
protected MockMvc mockMvc;
protected ObjectMapper objectMapper = new ObjectMapper();
#MockBean
private AppServiceImpl appService;
#Mock
private AppRegistrationRepository appRegistrationRepository;
#Before
public void setUp() throws Exception {
MapperFacade mapperFacade = new DefaultMapperFactory.Builder().build().getMapperFacade();
appService = new AppServiceImpl(appRegistrationRepository, mapperFacade);
}
What did I miss here?
Try to use
#RunWith(SpringRunner.class)
#WebMvcTest(YourController.class)
public class AppRestControllerTest {
Or
#RunWith(SpringRunner.class)
#SpringBootTest(webEnvironment = SpringBootTest.WebEnvironment.MOCK)
#AutoConfigureMockMvc
public class AppRestControllerTest {
In your tests

MockMvc perform 'get' do not create JSESSIONID cookie

I try to test my security layer using MockMvc. I've write the following integration test:
#RunWith(SpringJUnit4ClassRunner.class)
#ContextConfiguration(classes = {ApplicationContextConfig.class,WebSecurityConfig.class})
#WebAppConfiguration
public class AuthenticationStatesIT {
#Autowired
private WebApplicationContext context;
private MockMvc mockMvc;
#Before
public void initMocks(){
this.mockMvc = MockMvcBuilders.webAppContextSetup(context)
.addFilter(new AuthenticationFilter(), "/*")
.build();
}
#Test
public void stage10_firstRequestForLoginPageShouldReturnProperPageAndAddUnauthenticatedStateToSession () throws Exception {
MvcResult mvcResult = mockMvc.perform(get("/"))
.andDo(print())
//.andExpect(cookie().exists("JSESSIONID"))
.andExpect(status().is3xxRedirection()).andReturn();
MockHttpSession session = (MockHttpSession) mvcResult.getRequest().getSession();
StatesAuthenticator authenticator = (StatesAuthenticator)session.getAttribute("authenticator");
AuthenticationState state = authenticator.getState();
Assert.assertNotNull(authenticator);
Assert.assertNotNull(state);
}
}
Everything work ok except one detail. The 'JSESSIONID' cookie is not creating. I am sure that the new session is created but the test 'andExpect(cookie().exists("JSESSIONID"))' is not passed. I am creating session as follows:
public class UnauthenticatedState implements AuthenticationState {
#Override
public void doAuthentication(StatesAuthenticator authentication,ServletRequest request,
ServletResponse response,FilterChain chain) throws IOException, ServletException {
authentication.setAuthentication(null);
HttpServletResponse httpResponse = (HttpServletResponse)response;
HttpServletRequest httpRequest = (HttpServletRequest)request;
//get the old session and invalidate if exists
HttpSession oldSession = httpRequest.getSession(false);
if (oldSession != null) {
oldSession.invalidate();
}
//generate a new session
HttpSession session = httpRequest.getSession(true);
session.setMaxInactiveInterval(300); // 5 minutes
session.setAttribute("authenticator", authentication);
authentication.setState(new AuthenticatingState());
httpResponse.sendRedirect("login");
}
}
When I run server and look for that cookie in browser everything is ok, the cookie exists. Can someone explain me why MockMvc do no set 'JSESSIONID'? Thanks for any help!

spring boot unit test assertion error

Working on a spring boot based Rest project I have a controller like this
which calls service and service layer call dao layer. Now I am writing unit test code for controllers. when I run this the error says
java.lang.AssertionError: expected:<201> but was:<415>
I don't know where I am doing wrong:
public class CustomerController {
private static final Logger LOGGER = LogManager.getLogger(CustomerController.class);
#Autowired
private CustomerServices customerServices;
#Autowired
private Messages MESSAGES;
#Autowired
private LMSAuthenticationService authServices;
#RequestMapping(value = "/CreateCustomer", method = RequestMethod.POST)
public Status createCustomer(#RequestBody #Valid Customer customer, BindingResult bindingResult) {
LOGGER.info("createCustomer call is initiated");
if (bindingResult.hasErrors()) {
throw new BusinessException(bindingResult);
}
Status status = new Status();
try {
int rows = customerServices.create(customer);
if (rows > 0) {
status.setCode(ErrorCodeConstant.ERROR_CODE_SUCCESS);
status.setMessage(MESSAGES.CUSTOMER_CREATED_SUCCESSFULLY);
} else {
status.setCode(ErrorCodeConstant.ERROR_CODE_FAILED);
status.setMessage(MESSAGES.CUSTOMER_CREATION_FAILED);
}
} catch (Exception e) {
LOGGER.info("Cannot Create the Customer:", e);
status.setCode(ErrorCodeConstant.ERROR_CODE_FAILED);
status.setMessage(MESSAGES.CUSTOMER_CREATION_FAILED);
}
return status;
}
}
The test for the CustomerController.
public class CustomerControllerTest extends ApplicationTest {
private static final Logger LOGGER = LogManager.getLogger(CustomerControllerTest.class);
#Autowired
private WebApplicationContext webApplicationContext;
private MockMvc mockMvc;
#MockBean
private CustomerController customerController;
#Before
public void setup() {
this.mockMvc = MockMvcBuilders.webAppContextSetup(webApplicationContext).build();
}
Status status = new Status(200,"customer created successfully","success");
String customer = "{\"customerFullName\":\"trial8900\",\"customerPhoneNumber\": \"trial8900\", \"customerEmailID\": \"trial8900#g.com\",\"alternateNumber\": \"trial8900\",\"city\": \"trial8900\",\"address\":\"hsr\"}";
#Test
public void testCreateCustomer() throws Exception {
String URL = "http://localhost:8080/lms/customer/CreateCustomer";
Mockito.when(customerController.createCustomer(Mockito.any(Customer.class),(BindingResult) Mockito.any(Object.class))).thenReturn(status);
// execute
MvcResult result = mockMvc.perform(MockMvcRequestBuilders.post(URL)
.contentType(MediaType.APPLICATION_JSON_UTF8)
.accept(MediaType.APPLICATION_JSON_UTF8)
.content(TestUtils.convertObjectToJsonBytes(customer))).andReturn();
LOGGER.info(TestUtils.convertObjectToJsonBytes(customer));
// verify
MockHttpServletResponse response = result.getResponse();
LOGGER.info(response);
int status = result.getResponse().getStatus();
LOGGER.info(status);
assertEquals(HttpStatus.CREATED.value(), status);
}
}
HTTP status 415 is "Unsupported Media Type". Your endpoint should be marked with an #Consumes (and possibly also #Produces) annotation specifying what kinds of media types it expects from the client, and what kind of media type it returns to the client.
Since I see your test code exercising your production code with MediaType.APPLICATION_JSON_UTF8, you should probably mark your endpoint as consuming and producing APPLICATION_JSON_UTF8.
Then you also need to make sure that there is nothing terribly wrong going on in your error handling, because in the process of catching the exceptions generated by your production code and generating HTTP responses, your error handling code may be generating something different, e.g. generating an error status response with a payload containing an HTML-formatted error message, which would have a content-type of "text/html", which would not be understood by your test code which expects json.
Use the below base test class for your setUp and converting json to string and string to json
#RunWith(SpringJUnit4ClassRunner.class)
#SpringBootTest(classes = Main.class)
#WebAppConfiguration
public abstract class BaseTest {
protected MockMvc mvc;
#Autowired
WebApplicationContext webApplicationContext;
protected void setUp() {
mvc = MockMvcBuilders.webAppContextSetup(webApplicationContext).build();
}
protected String mapToJson(Object obj) throws JsonProcessingException {
ObjectMapper objectMapper = new ObjectMapper();
return objectMapper.writeValueAsString(obj);
}
protected <T> T mapFromJson(String json, Class<T> clazz)
throws JsonParseException, JsonMappingException, IOException {
ObjectMapper objectMapper = new ObjectMapper();
return objectMapper.readValue(json, clazz);
}
}
Also verify that your post call has happened or not check the below sample
Mockito.doNothing().when(customerServices).create(Mockito.any(Customer.class));
customerServices.create(customer);
Mockito.verify(customerServices, Mockito.times(1)).create(customer);
RequestBuilder requestBuilder = MockMvcRequestBuilders.post(URI)
.accept(MediaType.APPLICATION_JSON).content(inputInJson)
.contentType(MediaType.APPLICATION_JSON);
MvcResult mvcResult = mvc.perform(requestBuilder).andReturn();
MockHttpServletResponse response = mvcResult.getResponse();
assertEquals(HttpStatus.OK.value(), response.getStatus());

Spring Boot & OAuth2: Getting ResourceAccessException: I/O error on POST request for "http://localhost:5555/oauth/token": Connection refused: connect

based on this example https://github.com/royclarkson/spring-rest-service-oauth I have modified the source code adding repositories and other rest controllers. The resource and authorization servers are running/configured in the same application.
I have added an endpoint "/api/login" (POST) with request body:
{"usernamme":"foo", "password":"bar"}
I want to get an access token for this user in the service called by the controller. This implementation looks as follows:
Controller
public ResponseEntity<OAuth2AccessToken> login(#RequestBody #Valid final LoginCommand credentials)
throws LoginFailedException, UnexpectedErrorException {
try {
final OAuth2AccessToken token = securityService.authenticate(credentials.getUsername(), credentials.getPassword());
return ResponseEntity.ok(token);
} catch (final InvalidGrantException badCredentialExeption) {
throw new LoginFailedException(badCredentialExeption.getMessage());
} catch (final Exception e) {
throw new UnexpectedErrorException(e.getMessage());
}
}
Service
#Autowired
#Qualifier("OAuth2RestOperations")
private OAuth2RestOperations client;
#Override
public OAuth2AccessToken authenticate(final String username, final String password) {
final ResourceOwnerPasswordResourceDetails resourceDetails = (ResourceOwnerPasswordResourceDetails) client.getResource();
resourceDetails.setUsername(username);
resourceDetails.setPassword(password);
return client.getAccessToken();
}
Rest client config
#Configuration
#Import({ OauthProperties2.class })
#EnableOAuth2Client
public class RestClientConfig {
#Autowired
private OauthProperties2 oauth;
#Bean(name = "OAuth2RestOperations")
public OAuth2RestOperations restTemplate(final OAuth2ClientContext oauth2ClientContext) {
return new OAuth2RestTemplate(resource(), oauth2ClientContext);
}
#Bean
public OAuth2ProtectedResourceDetails resource() {
final ResourceOwnerPasswordResourceDetails resource = new ResourceOwnerPasswordResourceDetails();
resource.setAccessTokenUri(oauth.getClient().getAccessTokenUri());
resource.setClientId(oauth.getClient().getClientId());
resource.setClientSecret(oauth.getClient().getClientSecret());
resource.setGrantType(oauth.getClient().getGrantType());
resource.setScope(oauth.getClient().getScope());
return resource;
}
}
The Test
public class SecurityApiControllerTest extends TestBase {
#InjectMocks
private SecurityApiController controller;
#Test
public void loginOK() throws Exception {
final String credentials = FileUtils.readContent("requests/loginOK.json");
// #formatter:off
mvc.perform(post("/api/login")
.contentType(MediaType.APPLICATION_JSON_VALUE)
.content(credentials))
.andExpect(status().isOk());
// #formatter:on
}
}
TestBase
#RunWith(SpringJUnit4ClassRunner.class)
#WebAppConfiguration
#SpringBootTest(classes = Application.class)
public class TestBase {
#Autowired
protected WebApplicationContext context;
#Autowired
protected FilterChainProxy springSecurityFilterChain;
protected MockMvc mvc;
#Before
public void setUp() {
MockitoAnnotations.initMocks(this);
mvc = MockMvcBuilders.webAppContextSetup(context).addFilter(springSecurityFilterChain).build();
}
}
When running the application, I can call the endpoint for example with POSTMAN. During the test I get a connection refused as described in question header. I have tried to figure out the reason why the test is not working. Can anybody give me a hint to solve this issue?
It is so weird. It works since I have changed the TestBase class to:
#RunWith(SpringJUnit4ClassRunner.class)
// #WebAppConfiguration
#ContextConfiguration(classes = Application.class)
#SpringBootTest(webEnvironment = WebEnvironment.DEFINED_PORT)

Categories