#WithMockUser doesn't work in Integration Test - Spring boot - java

I am still getting Access Denied although my test method is annotated with #WithMockUser. Why this is not working in integration test? Everything is fine with test with #WebAppConfiguration and MockMvc.
Test Class:
#RunWith(SpringRunner.class)
#SpringBootTest(webEnvironment = SpringBootTest.WebEnvironment.RANDOM_PORT)
public class FileUploadIntegrationTest {
#Autowired
private TestRestTemplate restTemplate;
#MockBean
private FileStorageService storageService;
#Test
public void classPathResourceTest() throws Exception {
ClassPathResource resource = new ClassPathResource("/test/testFile.txt", getClass());
assertThat(resource.exists(), is(true));
}
#Test
#WithMockUser(username="tester",roles={"USER"})
public void shouldUploadFile() throws Exception {
ClassPathResource resource = new ClassPathResource("/test/testFile.txt", getClass());
MultiValueMap<String, Object> map = new LinkedMultiValueMap<String, Object>();
map.add("file", resource);
ResponseEntity<String> response = this.restTemplate.postForEntity("/files", map, String.class);
// assertThat(response.getStatusCode(), is(HttpStatus.OK));
then(storageService).should().addFile((any(String.class)), any(MultipartFile.class));
}
}
Controller class:
#RestController
#RequestMapping("/files")
#PreAuthorize(value = "hasRole('ROLE_USER')")
public class FileUploadController {
private FileStorageService fileStorageService;
private AuthenticationFacade authenticationFacade;
#Autowired
public FileUploadController(FileStorageService fileUploadService, AuthenticationFacade authenticationFacade) {
this.fileStorageService = fileUploadService;
this.authenticationFacade = authenticationFacade;
}
#ResponseBody
#PostMapping
public ResponseEntity<UUID> uploadFile(#RequestParam("file") MultipartFile file) {
UUID uuid = this.fileStorageService.addFile(authenticationFacade.getAuthentication().getName(), file);
if (uuid != null) return ResponseEntity.ok(uuid);
else return (ResponseEntity<UUID>) ResponseEntity.badRequest();
}
}

Couldn't solve this with #WithMockUser.
You can try using the Profiles approach described here: https://stackoverflow.com/a/35192495/3010484.

Related

No qualifying bean of type 'org.springframework.boot.actuate.health.HealthEndpoint' in controller test

I wrote a controller which combines actuator info.
#RestController
#Slf4j
public class AppStatusRestController {
private final HealthEndpoint healthEndpoint;
private final InfoEndpoint infoEndpoint;
public AppStatusRestController(HealthEndpoint healthEndpoint, InfoEndpoint infoEndpoint) {
this.healthEndpoint = healthEndpoint;
this.infoEndpoint = infoEndpoint;
}
#GetMapping("/status")
public Status status() {
Map<String, Object> info = infoEndpoint.info();
return Status.builder()
.status(healthEndpoint.health().getStatus().getCode())
.appName("My application")
.version(((Map<String, Object>) info.get("build")).get("version").toString())
.buildDateTime(((Map<String, Object>) info.get("build")).get("timestamp").toString())
.build();
}
}
In my test I get an error No qualifying bean of type 'org.springframework.boot.actuate.health.HealthEndpoint'.
#SpringBootTest(classes = AppStatusRestController.class)
#TestPropertySource(properties = "management.endpoints.web.exposure.include=*")
class AppStatusRestControllerTest {
#Test
void status() {
}
}
How can I actiavate default spring actuator beans in controller test(#SpringBootTest/#WebMvcTest)?
I guess I'm narrowing down the context is the answer to your question: Spring includes only the controller into its context skipping everything else. Try to include HealthEndpointAutoConfiguration too.
You can use #SpringBootTest() with no classes and it will work too. In this case, Spring will load the full context for executing the tests.
#SpringBootTest()
#AutoConfigureMockMvc
#TestPropertySource(properties = { "management.endpoints.web.exposure.include=health,info" })
class AppStatusRestControllerTest {
#Autowired
private MockMvc mockMvc;
#Test
void version() throws Exception {
MvcResult mvcResult = this.mockMvc.perform(get("/status")).andReturn();
String contentAsString = mvcResult.getResponse().getContentAsString();
ObjectMapper objectMapper = new ObjectMapper();
Status result = objectMapper.readValue(contentAsString, Status.class);
assertEquals(result.getStatus(), "UP");
}
}
I fixed the problem by creating TestApplication.java which includes only one controller of my application. Then I'm testing by loading test app into context #SpringBootTest(classes = TestApplication.class).
#SpringBootApplication
#Import(AppStatusRestController.class)
public class TestApplication {
public static void main(String[] args) {
SpringApplication.run(TestApplication.class, args);
}
}
#SpringBootTest(classes = TestApplication.class)
#AutoConfigureMockMvc
#TestPropertySource(properties = { "management.endpoints.web.exposure.include=health,info" })
class AppStatusRestControllerTest {
#Autowired
private MockMvc mockMvc;
#Test
void version() throws Exception {
MvcResult mvcResult = this.mockMvc.perform(get("/status")).andReturn();
String contentAsString = mvcResult.getResponse().getContentAsString();
ObjectMapper objectMapper = new ObjectMapper();
Status result = objectMapper.readValue(contentAsString, Status.class);
assertEquals(result.getStatus(), "UP");
}
}

Integration test not able to mock for methods annotated with #CircuitBreaker

Integration test not able to mock RestTemplate field on methods annotated with #CircuitBreaker.
controller:
#RestController
public class CollegeController {
#Autowired
private CollegeService collegeService;
#RequestMapping(value = "/college/student/{collegeId}")
public ResponseEntity<Map> getCollegeStudent(#PathVariable String collegeId){
ResponseEntity<Map> studentByCollege = collegeService.getStudentByCollege(collegeId);
return studentByCollege;
}
}
Service:
#Service
public class CollegeServiceImpl implements CollegeService {
public static final String COLLEGE_SERVICE = "collegeService";
#Autowired
RestTemplate restTemplate;
int count = 0;
#Override
#Retry(name = COLLEGE_SERVICE/*, fallbackMethod = "fallbackForRetry"*/)
#CircuitBreaker(name = COLLEGE_SERVICE, fallbackMethod = "getAllStudentFallback")
public ResponseEntity<Map> getStudentByCollege(String collegeId) {
ResponseEntity<Map> forEntity = null;
String url = "http://localhost:8080/student/{collegeId}";
System.out.println(" count = " + ++count);
HttpHeaders headers = new HttpHeaders();
HttpEntity request = new HttpEntity(headers);
forEntity = restTemplate.exchange(url, HttpMethod.GET,request, Map.class,collegeId);
//return testCircuitBreakerInFunctionalProgramming().apply(collegeId);
//throw new RuntimeException("abc");
return forEntity;
}
public ResponseEntity<Map> getAllStudentFallback(String collegeId, Throwable t) {
System.out.println("fallback method");
return null;
}
public ResponseEntity<Map> getAllStudentFallback(String collegeId, CallNotPermittedException t) {
System.out.println("fallback call not permitted method");
return null;
}
Integration Test :
#SpringBootTest
#RunWith(SpringRunner.class)
public class CollegeIntegrationTest {
//#InjectMocks
#Autowired
private CollegeController controller;
#InjectMocks
#Autowired
private CollegeServiceImpl service;
#Mock
private RestTemplate restTemplate;
#Before
public void setup() {
MockitoAnnotations.openMocks(this);
}
#Test
public void testCollege() {
ResponseEntity<Map> response = new ResponseEntity<>(HttpStatus.OK);
Mockito.when(restTemplate.exchange(Mockito.anyString(), Mockito.same(HttpMethod.GET),Mockito.<HttpEntity> any(), Mockito.any(Class.class),Mockito.anyString())).thenReturn(response);
controller.getCollegeStudent("102");
}
}
What I am doing:
1 of the micorservice is down and I am implementing CircuitBreaker for that scenario and Its working as expected.
Issue:
When I am not using CircuitBreaker annotation, mock of RestTemplate is available with same id in service class. i.e Mocking working fine as expected.
In case when I use #CircuitBreaker, some mock id is generated in IntegrationTest class but the same is not available on service, so not able to mock restcall.
I am initializing rest template using :
#Bean
public RestTemplate restTemplate() {
RestTemplate restTemplate = new RestTemplate();
return restTemplate;
}
In whole mocking is not working as expected while using #CircuitBreaker.
Any suggestion would be appreciated.

No mapping for request with mockmvc

Currently struggling with problem when I get 'mapping error for request' with following controller/test configuration.
Controller:
#Slf4j
#Validated
#RestController
#RequiredArgsConstructor
public class AdtechController {
private final AdtechService adtechService;
#PostMapping(value = "/subscriber/session")
public ResponseEntity<ResponseDto> submitSession(#RequestBody RequestDto requestDto) {
log.trace("execute submitSession with {}", requestDto);
ResponseDtoresponse = adtechService.submitSession(requestDto);
return new ResponseEntity<>(response, HttpStatus.OK);
}
#ExceptionHandler(AdtechServiceException.class)
public ResponseEntity<AdtechErrorResponse> handleAdtechServiceException(AdtechServiceException e) {
return new ResponseEntity<>(new AdtechErrorResponse(HttpStatus.INTERNAL_SERVER_ERROR.value(), e.getMessage()), HttpStatus.INTERNAL_SERVER_ERROR);
}
}
Test:
#RunWith(SpringRunner.class)
#SpringBootTest
#AutoConfigureMockMvc
#SpringJUnitConfig({AdtechTestConfig.class})
public class AdtechControllerTest {
private static final ObjectMapper OBJECT_MAPPER = JsonUtil.getJackson();
#Autowired
private MockMvc mockMvc;
#Test
public void testSubmitSession() throws Exception {
RequestDto requestDto = new RequestDto ();
requestDto.setKyivstarId("1123134");
requestDto.setMsisdn("123476345242");
requestDto.setPartnerId("112432523");
requestDto.setPartnerName("125798756");
String request = OBJECT_MAPPER.writeValueAsString(requestDto);
System.out.println("REQUEST: " + request);
String response = OBJECT_MAPPER.writeValueAsString(new ResponseDto("123"));
System.out.println("RESPONSE: " + response);
mockMvc.perform(post("/subscriber/session")
.content(MediaType.APPLICATION_JSON_VALUE)
.content(request))
.andDo(print())
.andExpect(status().isOk())
.andExpect(content().string(containsString(response)));
}
}
Configuration:
#Configuration
public class AdtechTestConfig {
#Bean
public AdtechService adtechTestService() {
return requestDto -> new AdtechResponseDto("123");
}
}
After test execution I get No mapping for POST /subscriber/session
The reason for the struggle is that my code from other modules with the same configuration works fine. Can somebody point out what am I missing ? Thanks in advance!
Apparently you are loading a configuration class to mock beans, this interferes with the other parts of Spring Boot and probably leads to partially loading your application. I suspect only the mocked service is available.
Instead of the test configuration use #MockBean to create a mock for the service and register behaviour on it.
#SpringBootTest
#AutoConfigureMockMvc
public class AdtechControllerTest {
private static final ObjectMapper OBJECT_MAPPER = JsonUtil.getJackson();
#Autowired
private MockMvc mockMvc;
#MockBean
private AdtechService mockService;
#BeforeEach
public void setUp() {
when(mockService.yourMethod(any()).thenReturn(new AdtechResponseDto("123"));
}
#Test
public void testSubmitSession() throws Exception {
// Your original test method
}
}
If the only thing you want to test is your controller you might also want to consider using #WebMvcTest instead of #SpringBootTest.
#WebMvcTest(AdTechController.class)
public class AdtechControllerTest {
private static final ObjectMapper OBJECT_MAPPER = JsonUtil.getJackson();
#Autowired
private MockMvc mockMvc;
#MockBean
private AdtechService mockService;
#BeforeEach
public void setUp() {
when(mockService.yourMethod(any()).thenReturn(new AdtechResponseDto("123"));
}
#Test
public void testSubmitSession() throws Exception {
// Your original test method
}
}
This will load a scaled-down version of the context (only the web parts) and will be quicker to run.
try this:
#Slf4j
#Validated
#RestController
#RequiredArgsConstructor
public class AdtechController {
private AdtechService adtechService;
public AdtechController (AdtechService adtechService) {
this.adtechService= adtechService;
}
#PostMapping(value = "/subscriber/session")
public ResponseEntity<ResponseDto> submitSession(#RequestBody RequestDto requestDto) {
log.trace("execute submitSession with {}", requestDto);
ResponseDtoresponse = adtechService.submitSession(requestDto);
return new ResponseEntity<>(response, HttpStatus.OK);
}
#ExceptionHandler(AdtechServiceException.class)
public ResponseEntity<AdtechErrorResponse> handleAdtechServiceException(AdtechServiceException e) {
return new ResponseEntity<>(new AdtechErrorResponse(HttpStatus.INTERNAL_SERVER_ERROR.value(), e.getMessage()), HttpStatus.INTERNAL_SERVER_ERROR);
}
}
Test:
#RunWith(SpringRunner.class)
#SpringBootTest
#AutoConfigureMockMvc
#SpringJUnitConfig({AdtechTestConfig.class})
public class AdtechControllerTest {
private static final ObjectMapper OBJECT_MAPPER = JsonUtil.getJackson();
#Autowired
private MockMvc mockMvc;
#Autowired
private AdtechService adtechService;
#Before
public void setUp() {
MockitoAnnotations.initMocks(this);
this.mvc = MockMvcBuilders.standaloneSetup(new AdtechController(adtechService)).build();
}
#Test
public void testSubmitSession() throws Exception {
RequestDto requestDto = new RequestDto ();
requestDto.setKyivstarId("1123134");
requestDto.setMsisdn("123476345242");
requestDto.setPartnerId("112432523");
requestDto.setPartnerName("125798756");
String request = OBJECT_MAPPER.writeValueAsString(requestDto);
System.out.println("REQUEST: " + request);
String response = OBJECT_MAPPER.writeValueAsString(new ResponseDto("123"));
System.out.println("RESPONSE: " + response);
mockMvc.perform(post("/subscriber/session")
.content(MediaType.APPLICATION_JSON_VALUE)
.content(request))
.andDo(print())
.andExpect(status().isOk())
.andExpect(content().string(containsString(response)));
}
}
Is the AdtechTestConfig.class introducing the /ad-tech path segment in to your test request? If so, this is why your test is trying the path /ad-tech/subscriber/session instead of /subscriber/session.
If this is actually the correct uri, then you may add #RequestMapping to the controller like below or just to the post method itself
#Slf4j
#Validated
#RestController
#RequestMapping("/ad-tech")
#RequiredArgsConstructor
public class AdtechController {
private final AdtechService adtechService;
#PostMapping(value = "/subscriber/session")
public ResponseEntity<ResponseDto> submitSession(#RequestBody RequestDto requestDto) {
...

TestRestTemplate in junit

ArticleController.java
#GetMapping(path = "articles/{article-id}")
public ResponseEntity<Article> getArticleById( #PathVariable("article-id") Long id) {
Article article = articleService.findById(id);
if (article != null)
return new ResponseEntity<>(article, HttpStatus.OK);
return new ResponseEntity<>(HttpStatus.NOT_FOUND);
}
ArticleControllerTest.java
public class ArticleControllerTest {
#Autowired
private TestRestTemplate template;
#Test
public void testGetArticle(){
// how to implement it
}
private HttpEntity<Object> getHttpEntity(Object body) {
HttpHeaders headers = new HttpHeaders();
headers.setContentType(MediaType.APPLICATION_JSON);
return new HttpEntity<Object>(body, headers);
}
}
i searched a lot but find nothing .... how to implement getArticle using the template and this private method ??
Your controller has a dependency to service class. And your method which you want to test is using articleService.findById(id) method. By mocking your service class you can test your controller.
Here is an example of how to use MockMvc:
#RunWith(SpringRunner.class)
#WebMvcTest(ProductController.class)
public class ProductControllerMvcTest {
#Autowired
private MockMvc mockMvc;
#MockBean
private ProductService productService;
#Test
public void it_should_return_product_by_id() throws Exception {
//given
Long productId = 1L;
Product product = ProductBuilder.aProduct().id(productId).title("Nokia").build();
given(productService.findById(productId)).willReturn(product);
//when
mockMvc.perform(get("/product/1"))
.andDo(print())
.andExpect(status().isOk())
.andExpect(jsonPath("id").value(productId))
.andExpect(jsonPath("title").value("Nokia"));
}}

How to set the context path in Spring Boot WebMvcTest

I'm trying to test my Rest controllers from my Spring Boot application and want the controllers to be available under the same path as in production.
For example I have the following Controller:
#RestController
#Transactional
public class MyController {
private final MyRepository repository;
#Autowired
public MyController(MyRepository repository) {
this.repository = repository;
}
#RequestMapping(value = "/myentity/{id}",
method = RequestMethod.GET,
produces = MediaType.APPLICATION_JSON_VALUE)
#ResponseBody
public ResponseEntity<Resource<MyEntity>> getMyEntity(
#PathVariable(value = "id") Long id) {
MyEntity entity = repository.findOne(id);
if (entity == null) {
return new ResponseEntity<>(HttpStatus.NOT_FOUND);
}
return new ResponseEntity<>(entity, HttpStatus.OK);
}
}
Within my application.yml I have configured the context path for the application:
server:
contextPath: /testctx
My test for this controller looks as follows:
#RunWith(SpringRunner.class)
#WebMvcTest(controllers = MyController.class, secure=false)
public class MyControllerTest {
#Autowired
private MyRepository repositoryMock;
#Autowired
private MockMvc mvc;
#Test
public void testGet() throws Exception {
MyEntity entity = new MyEntity();
entity.setId(10L);
when(repositoryMock.findOne(10L)).thenReturn(entity);
MockHttpServletResponse response = this.mvc.perform(
MockMvcRequestBuilders.get("/testctx/myentity/10"))
.andReturn().getResponse();
assertEquals(response.getStatus(), 200);
}
#TestConfiguration
public static class TestConfig {
#Bean
MyRepository mockRepo() {
return mock(MyRepository.class);
}
}
}
This test fails since the status code is 404 for the call. If I call /myentity/10 it works. Unfortunately the rest call is initiated by a CDC-Test-Framework (pact) so I cannot change the requested path (containing the context path /testctx). So is there a way to tell spring boot test to start the rest endpoint with a defined context path also during testing?
You could try:
#WebMvcTest(controllers = {MyController.class})
#TestPropertySource(locations="classpath:application.properties")
class MyControllerTest {
#Autowired
protected MockMvc mockMvc;
#Value("${server.servlet.context-path}")
private String contextPath;
#BeforeEach
void setUp() {
assertThat(contextPath).isNotBlank();
((MockServletContext) mockMvc.getDispatcherServlet().getServletContext()).setContextPath(contextPath);
}
protected MockHttpServletRequestBuilder createGetRequest(String request) {
return get(contextPath + request).contextPath(contextPath)...
}

Categories

Resources