How to mock a method with custom annotation in Spring boot? - java

I have a a service which is protected by oAuth. This is the structure for my RestController:
#CrossOrigin(origins = "*", allowedHeaders = "*")
#RestController
public class MainController {
#PostMapping("/v1/api")
#CustomAuthorizer(RequestURI = "api", ResourceType = ResourceType.API, GrantAccess = GrantAccess.EXECUTE)
public ResponseEntity<List<Details>> api(
#ApiParam(value = "Provide here request body", required = true) #Valid #RequestBody Input input,
#Valid #RequestParam("tId") String tenantId, #RequestHeader("Authorization") String auth) {
}
This is my Test for the same
#RunWith(SpringRunner.class)
#SpringBootTest
public class MainControllerMockTest {
#WithMockUser( username = "test")
#Test
public void testApi() throws Exception {
Input input = new Input();
//Set input here
MvcResult result = mockMvc.perform(post("/v1/api?tId=test")
.content(inputJson).contentType(MediaType.APPLICATION_JSON).header(HttpHeaders.AUTHORIZATION, "Bearer eyJhbGciOiJIUzI1NiJ9.eyJzdWIiOiJsd2lsbGlhbXMxNiIsInJvbGVzIjoidXNlciIsImlhdCI6MTUxNDQ0OTgzM30.WKMQ_oPPiDcc6sGtMJ1Y9hlrAAc6U3xQLuEHyAnM1FU")
.with(SecurityMockMvcRequestPostProcessors.csrf()))
.andExpect(status().isOk()).andReturn();
System.out.println(result.getResponse().getContentAsString());
assertEquals(200, result.getResponse().getStatus());
}
}
Authentication is done by our custom oAuth library, So when /api is called it checks for permissions, is there any way i can bypass that ? I tried mocking that Code as well but it didn't work. Any suggestions would be great.

Related

UnsupportedMediaTypeException: Content type 'application/octet-stream' not supported for bodyType=java.util.Map<java.lang.String, java.lang.String>))

I am facing below error while running junit for controller. I have already set content-type as Json, still error is same. Any suggestion what could be the issue ?
Error is
java.lang.AssertionError: expectation "expectNext({response=employee saved Successfully})" failed (expected: onNext({response=employee saved Successfully}); actual: onError(org.springframework.web.reactive.function.UnsupportedMediaTypeException: Content type 'application/octet-stream' not supported for bodyType=java.util.Map<java.lang.String, java.lang.String>))
Controller is
#Slf4j
#Controller
#RequiredArgsConstructor
public class EmployeeController {
private final EmployeeService employeeService;
#PostMapping(path ="/employees",produces = APPLICATION_JSON_VALUE)
public #ResponseBody Mono<Map<String, String>> saveEmployees(#RequestBody List<EmployeeDto> employeeDtos) {
log.info("Received request to save employees [{}]", employeeDtos);
return employeeService.saveEmployee(employeeDtos);
}
}
Service class as below:
#Service
#Slf4j
#RequiredArgsConstructor
public class EmployeeService {
private final WebClient webClient;
public Mono<Map<String, String>> saveEmployees(List<EmployeeDto> employeeDtos) {
return webClient
.post()
.uri("/create-employees")
.contentType(APPLICATION_JSON)
.bodyValue(employeeDtos)
.retrieve()
.bodyToMono(new ParameterizedTypeReference<Map<String, String>>() {
})
.doOnError(e -> log.error("Failed to save employees {}: {}", employeeDtos, e));
Junit is as below:
#Slf4j
#SpringBootTest
class EmployeeServiceTest {
private static final WireMockServer wireMockServer = new WireMockServer(wireMockConfig().dynamicPort());
#Autowired
private ObjectMapper objectMapper;
#Autowired
private EmployeeService employeeService;
#Test
void shouldMakeAPostApiCallToSaveEmployee() throws JsonProcessingException {
var actualemployeeDtos = "....";
var randomEmployeeDto = ...;
wireMockServer.stubFor(post("/create-employees")
.withHeader(CONTENT_TYPE, equalTo(APPLICATION_JSON_VALUE))
.withHeader(ACCEPT, containing(APPLICATION_JSON_VALUE))
.withRequestBody(equalToJson(actualemployeeDtos))
.willReturn(aResponse()
.withStatus(OK.value())
.withBody("{\"response\": \"employee saved Successfully\"}")));
StepVerifier
.create(employeeService.saveEmployee(List.of(randomEmployeeDto)))
.expectNext(singletonMap("response", "employee saved Successfully"))
.verifyComplete();
}
}
After debugging , i found that even response header need to be set with content-type as readWithMessageReaders () method of BodyExtractors class check for content-type.
.withHeader(CONTENT_TYPE, APPLICATION_JSON_VALUE)
set in below code fixed the failing testcase
#Test
void shouldMakeAPostApiCallToSaveEmployee() throws JsonProcessingException {
var actualemployeeDtos = "....";
var randomEmployeeDto = ...;
wireMockServer.stubFor(post("/create-employees")
.withHeader(CONTENT_TYPE, equalTo(APPLICATION_JSON_VALUE))
.withHeader(ACCEPT, containing(APPLICATION_JSON_VALUE))
.withRequestBody(equalToJson(actualemployeeDtos))
.willReturn(aResponse()
.withHeader(CONTENT_TYPE, APPLICATION_JSON_VALUE)
.withStatus(OK.value())
.withBody("{\"response\": \"Employees saved Successfully\"}")));
StepVerifier
.create(employeeService.saveEmployee(List.of(randomEmployeeDto)))
.expectNext(singletonMap("response", "Employees saved Successfully"))
.verifyComplete();
}
In my case I fixed the issue parsing manually one of the parts that contains a json object
This code did not work
#PostMapping(value = "salvaDomanda")
#ResponseBody
#ResponseStatus(HttpStatus.CREATED)
Domanda salvaDomanda(#RequestPart("data") Domanda domanda,
#RequestPart(name = "files", required = false) MultipartFile[] files)
This one
#PostMapping(value = "salvaDomanda")
#ResponseBody
#ResponseStatus(HttpStatus.CREATED)
Domanda salvaDomanda(#RequestPart("data") String jsonDomanda,
#RequestPart(name = "files", required = false) MultipartFile[] files)
throws ApplicationException, IOException {
ObjectMapper mapper = new ObjectMapper();
Domanda domanda = mapper.readValue(jsonDomanda, Domanda.class);
does.
My Spring boot version is 2.6.3 with java 17

Spring controller test fails despite url working

I am trying to test a basic controller:
#Autowired
DAOInterface db;
#RequestMapping(value = "/postdb", method = RequestMethod.GET)
#ResponseBody
public String postdb(
#RequestParam(value = "id", required = true) String id
) {
db.addEntry(id);
return "Added " + id + ".";
}
This url works as when I access it, it adds it to a db and I get the string output as a response.
I am trying to create a simple unit test for it:
#Autowired
MockMvc mockMvc;
#MockBean
DAOInterface daoInterface;
#Test
public void shouldReturnA200() throws Exception {
mockMvc.perform(get("/postdb?id=3"))
.andExpect(status().isOk());
}
But instead I get the following
MockHttpServletRequest:
HTTP Method = GET
Request URI = /postdb
Parameters = {id=[3]}
Headers = {}
Handler:
Type = org.springframework.web.servlet.resource.ResourceHttpRequestHandler
Async:
Async started = false
Async result = null
Resolved Exception:
Type = null
ModelAndView:
View name = null
View = null
Model = null
FlashMap:
Attributes = null
MockHttpServletResponse:
Status = 404
Error message = null
Headers = {}
Content type = null
Body =
Forwarded URL = null
Redirected URL = null
Cookies = []
java.lang.AssertionError: Status
Expected :200
Actual :404
Not sure why I it's working whenever I try and access it but fails when running this test. I don't see any issues. Might it be because I'm not using any headers or a formal response body/view and rather just outputting a String?
EDIT = It works when I add
.contextPath("/postdb")).. not sure if that's the correct way but when I write another test and dont include any request paramters, that test gives a 200 instead of a 400 or 404....
#Autowired
DAOInterface db;
#RequestMapping(value = "/postdb", method = RequestMethod.GET)
public ResponseEntity<String> postdb(#RequestParam(required = true) String id) {
db.addEntry(id);
return new ResponseEntity<>("Added " + id + ".", HttpStatus.OK);
}
Test:
#Test
public void shouldReturnA200() throws Exception {
mockMvc.perform(get("/postdb?id=3")
.contentType(MediaType.APPLICATION_JSON)
.accept(MediaType.APPLICATION_JSON))
.andExpect(status().isOk());
}
Below is working fine for me
public class FirstWebController {
#RequestMapping(value = "/postdb", method = RequestMethod.GET)
#ResponseBody
public String postdb(#RequestParam(value = "id", required = true) String id) {
System.out.println("idddddddddddd "+id);
return "Added " + id + ".";
}
}
Test class is
#RunWith(SpringJUnit4ClassRunner.class)
#ContextConfiguration
public class FirstWebControllerTest {
#Configuration
static class FirstWebControllerTestConfiguration {
#Bean
public FirstWebController firstWebController() {
return new FirstWebController();
}
}
#Autowired
private FirstWebController firstWebController;
private MockMvc mockMvc;
#Before
public void setup() {
mockMvc = standaloneSetup(firstWebController).build();
}
#Test
public void shouldReturnA200() throws Exception {
mockMvc.perform(get("/postdb?id=3")).andExpect(status().isOk());
}
}
Try adding query parameter as below:
#Test
public void shouldReturnA200() throws Exception {
mockMvc.perform(get("/postdb).param("id", "3"))
.andExpect(status().isOk());
}

Spring boot:Authorization using header before controller

I am using my spring boot REST API with the controller as follows:-
`
#Autowired
HazelcastInstance hazelcastinstance;
String username="VAKSDNDDODM#DLDM#DMOD#DI##*#EK";
#RestController
#RequestMapping(value = "/Acode/availabile/multiple/{Code}/")
public class MultiController {
#Resource(name = "AService")
protected AImpl AService;
#RequestMapping(method = RequestMethod.POST, produces = MediaType.APPLICATION_JSON_VALUE)
public ResponseEntity<Response> readMulti(
#RequestHeader(value="Auth-Token") String token,
#RequestBody XRequest Request,
#PathVariable String Code) throws Exception {
if(token.equalsIgnoreCase(username))
{
Response response = AService.readMultiX(Request, Code);
}
else
{
response.setMessage("Unauthorized access!");
response.setStatus(340);
}
return new ResponseEntity<Response>(response, HttpStatus.OK);
}
}
`
I wish to do the header comparision before API control enters the controller such that declaring #RequestHeader is not required in controller.Is it possible to do this or do we have an implementation for that?

How to specify #RequestMapping params in MockMvc

I have a controller:
#Controller
#RequestMapping(value = "/bookForm")
public class BookFormController {
#Autowired
private BookHttpRequestParser parser;
#Autowired
private BooksService booksService;
#RequestMapping(params = "add", method = RequestMethod.POST)
public String addBook(HttpServletRequest request) {
try {
Book newBook = parser.createBookFromRequest(request);
booksService.addBook(newBook);
} catch (InvalidTypedParametersException e) {
}
return "redirect:index.html";
}
This Controller has a method for adding book to DB. Method has #RequestMapping annotation with params = "add" value.
Im trying to set this params criteria to controller unit test method:
#Test
public void addBook() throws Exception{
HttpServletRequest request = mock(HttpServletRequest.class);
Book book = new Book();
when(parser.createBookFromRequest(request)).thenReturn(book);
mockMvc.perform(post("/bookForm", "add"))
.andExpect(status().isOk())
.andExpect(view().name("redirect:index.html"));
}
Where to specify this #ResuetsMapping params value?
This:
mockMvc.perform(post("/bookForm", "add"))
doesn't work at all.
The following should work.
mockMvc.perform(post("/bookForm?add="))
use RequestBuilder requestBuilders;
object to build your request
requestBuilders = MockMvcRequestBuilders.get("URL/{Pathvariable}","PathvariableValue")
.contentType(MediaType.APPLICATION_JSON)
.header("HeaderName", HeaderValue)
.param("ParameterName", "Value")
.param("ParameterName", "Value")
.accept(MediaType.APPLICATION_JSON);
and the perfrom
mockMvc.perform(requestBuilders)
.andDo(print())
.andExpect(status().isOk())
.andReturn();

Spring Testing - java.lang.IllegalArgumentException: Not enough variable values available to expand

I am writing Unit Tests for the below REST Controller which takes a UserID and grants a List of Authorities to that user.
#RestController
#RequestMapping("/user")
#Api(value = "User", description = "User API")
public class UserController{
// some code
#RequestMapping(method = RequestMethod.POST, value = "/{userId}/grantAuthz")
#ApiOperation(value = "GrantAuthz", notes = "Grant Authorization")
public Collection<UserEntity.UserAuthz> grantAuthz(#PathVariable("userId") String userId,
#RequestBody ArrayList<String> authorities) {
UserEntity userEntity = userRepository.findOne(userId);
if(userEntity == null) {
//TODO: throw and send resource not found
return null;
}
log.debug("Authorities to be granted to user " + userId + " are : " + authorities);
for(String authz : authorities) {
log.debug("Adding Authorization " + authz);
userEntity.addUserAuthz(authz);
}
userRepository.save(userEntity);
return userEntity.getAuthorities();
}
}
I wrote the below Unit Test for the UserController
#RunWith(SpringJUnit4ClassRunner.class)
#SpringApplicationConfiguration(classes = Application.class)
#WebAppConfiguration
public class UserControllerTest {
private final Log log = LogFactory.getLog(getClass());
private MediaType contentType = new MediaType(MediaType.APPLICATION_JSON.getType(),
MediaType.APPLICATION_JSON.getSubtype(),
Charset.forName("utf8"));
private MockMvc mockMvc;
private HttpMessageConverter mappingJackson2HttpMessageConverter;
private final String USER_URL = "/{userId}/grantAuthz";
private final String USER_ID = "111";
private final String USER_NAME = "MockUser";
#Autowired
private WebApplicationContext webApplicationContext;
#Autowired
private UserRepository userRepository;
private String createdToken = null;
#Autowired
void setConverters(HttpMessageConverter<?>[] converters) {
this.mappingJackson2HttpMessageConverter = Arrays.asList(converters).stream().filter(
hmc -> hmc instanceof MappingJackson2HttpMessageConverter).findAny().get();
Assert.assertNotNull("the JSON message converter must not be null",
this.mappingJackson2HttpMessageConverter);
}
#Before
public void setup() throws Exception {
this.mockMvc = webAppContextSetup(webApplicationContext).build();
}
#Test
public void testGrantAuthorizationForUser() throws Exception{
Optional<UserEntity> userEntityAuthz = userRepository.findOneByUsername(USER_NAME);
Set<String> expectedAuthzList = (LinkedHashSet)userEntityAuthz.get().getAuthorizations();
List<String> grantList = new ArrayList<>();
grantList.add("ABC");
grantList.add("DEF");
grantList.add("GHI");
grantList.add("JKL");
grantList.add("MNO");
grantList.add("PQR");
grantList.add("STU");
grantList.add("VWX");
grantList.add("YZA");
JSONObject json = new JSONObject();
json.put("grantList",grantList);
MvcResult grantAuthzResult = mockMvc.perform(MockMvcRequestBuilders.post(USER_URL)
.contentType(contentType)
.param("userId",USER_ID)
.param("authorities",json.toString()))
.andExpect(status().isOk())
.andDo(print())
.andReturn();
}
}
When executed, my test is throwing an Illegal Argument Exception:
"Not enough variable values available to expand 'userId'"
I am sending the required URL Parameters using the .param() method in the test, what am I doing wrong ? I reffered this possible duplicate question but did not find it much useful. Using RestTemplate in Spring. Exception- Not enough variables available to expand
I found out what I am doing wrong, using param() method is not the right way here as I have #PathVariable and #RequestBody in my Controller Methods as the parameters.
public Collection<UserEntity.UserAuthz> grantAuthz(#PathVariable("userId") String userId,
#RequestBody ArrayList<String> authorities) {
So I passed the #PathVariable in the post() method of the test.
MockMvcRequestBuilders.post(USER_URL,USER_ID)
As the required type is #RequestBody ArrayList<String> instead of using the JSONObject I used JSONArrayand used the content() method to send the JSONArray as the string.
Here are the changes I have made to the Test Method.
#Test
public void testGrantAuthorizationForUser() throws Exception{
Optional<UserEntity> userEntityAuthz = userRepository.findOneByUsername(USER_NAME);
Set<String> expectedAuthzList = (LinkedHashSet)userEntityAuthz.get().getAuthorizations();
List<String> grantList = new ArrayList<>();
grantList.add("ABC");
grantList.add("DEF");
grantList.add("GHI");
grantList.add("JKL");
grantList.add("MNO");
grantList.add("PQR");
grantList.add("STU");
grantList.add("VWX");
grantList.add("YZA");
JSONArray json = new JSONArray();
MvcResult grantAuthzResult = mockMvc.perform(MockMvcRequestBuilders.post(USER_URL,USER_ID)
.contentType(contentType)
.content(json.toString()))
.andExpect(status().isOk())
.andDo(print())
.andReturn();
}
#Test
public void getOneContactAPI() throws Exception {
String id = "8";
mvc.perform(MockMvcRequestBuilders.get("/api/contact/{id}",id).accept(MediaType.APPLICATION_JSON))
.andDo(MockMvcResultHandlers.print())
.andExpect(status().isOk())
.andExpect(MockMvcResultMatchers.jsonPath("id").exists());
}

Categories