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

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());
}

Related

java.lang.AssertionError: Status 404

Im having 404 error on this test , Im trying to figure out why its giving me this error could someone help me please
this is my controller :
#PostMapping("/infoUtile/add/{idUser}")
public InformationUtile addOrEditProcedure(#PathVariable(value = "idUser") Long id){
User user = this.userService.getUser(id);
InformationUtile info = new InformationUtile();
info.setUser(user);
return this.infoUtileService.addOrEditInfoUtile(info);
}
and this is the test that I wrote
#Autowired
private MockMvc mockMvc;
#MockBean
InformationUtileService informationUtileService;
#MockBean
UserService userService;
ObjectMapper mapper = new ObjectMapper();
#Test
public void givenInformationsUtiles_whenGetInfoUtiles_thenReturnJsonArray()
throws Exception {
InformationUtile informationUtile = new InformationUtile();
informationUtile.setId(1);
informationUtile.setContent("oumaima");
informationUtile.setDescription("test");
Media medias = new Media();
medias.setType("image/png");
medias.setUrl("C:\\files\\actualite\\32769\\adobexd.png");
List<Media> allMedias = new ArrayList<Media>();
allMedias.add(medias);
informationUtile.setMedias(allMedias);
OngoingStubbing<User> user = Mockito.when(userService.getUser(Mockito.anyLong())).thenReturn(new User());
Mockito.when(informationUtileService.addOrEditInfoUtile(Mockito.any(InformationUtile.class))).thenReturn(informationUtile);
mockMvc.perform(post("/infoUtile/add/{id}",informationUtile.getId())
.contentType(MediaType.APPLICATION_JSON)
.content(mapper.writeValueAsBytes(informationUtile)))
.andExpect(status().isOk());
}
log
I had the same issue and in my case the solution was to use full path
from #RequestMapping (controller annotation) + #PostMapping (method annotation)
I don't know if this is the case for you. You don't show your controller class. But if you have something like this:
#RestController
#RequestMapping("/api/v1")
public class SomeController {
#PostMapping("/infoUtile/add/{idUser}")
public InformationUtile addOrEditProcedure(#PathVariable(value = "idUser") Long id) {...}
}
then the path in test will be
mockMvc.perform(post("/api/v1/infoUtile/add/{idUser}",informationUtile.getId())

Not able to call the controller method using MockMvc

I am not able to get how to call the controller method(API) in this case. How to send the MultipartFile as request parameter and how to pass the HttpServletRequest so that mockMvc can call the actual method for testing.
#RunWith(SpringRunner.class)
public class PartnerSiteLoadControllerTest {
private MockMvc mockMvc;
#Mock
private PartnerSiteUploadService partnerSiteUploadService;
#InjectMocks
private PartnerSiteLoadController partnerSiteLoadController;
#Before
public void setup() {
this.mockMvc = MockMvcBuilders.standaloneSetup(partnerSiteLoadController)
.setControllerAdvice(new PartnerExceptionHandlerMapper()).build();
}
#Test
public void uploadSitesInBulk()throws Exception {
String userId = "userId";
HttpServletRequest request = mock(HttpServletRequest.class);
UserPrincipal userPrincipal = new UserPrincipal();
userPrincipal.setId("id");
BulkUploadResponseDTO bulkUploadResponseDTO = new BulkUploadResponseDTO();
FileInputStream inputFile = new FileInputStream( "src/test/resources/PartnerSites_2019-09-04_v1.xlsx");
MockMultipartFile file = new MockMultipartFile("PartnerSites_2019-09-04_v1.xlsx", "PartnerSites_2019-09-04_v1.xlsx", "multipart/form-data", inputFile);
when(file.getOriginalFilename()).thenReturn("PartnerSites_2019-09-04_v1.xlsx");
when(request.getAttribute("userPrincipal")).thenReturn(userPrincipal);
when(partnerSiteUploadService.uploadSitesInBulk(userId,file)).thenReturn(bulkUploadResponseDTO);
mockMvc.perform(MockMvcRequestBuilders.fileUpload("/v4/slm/partners/sites/import")
.file(file).contentType(MediaType.MULTIPART_FORM_DATA).requestAttr("userPrincipal",userPrincipal))
.andExpect(status().isOk());
verify(partnerSiteUploadService,times(1)).uploadSitesInBulk(userId,file);
verifyNoMoreInteractions(partnerSiteUploadService);
}
}
Controller class method
#RestController
#RequestMapping("/v4/slm/partners/sites/import")
#Api(value = "Site Bulk Upload Service")
#Slf4j
#Validated
public class PartnerSiteLoadController {
private PartnerSiteUploadService partnerSiteUploadService;
#Autowired
public PartnerSiteLoadController(PartnerSiteUploadService partnerSiteUploadService) {
this.partnerSiteUploadService = partnerSiteUploadService;
}
#PostMapping(value = "", headers = ("content-type=multipart/*"))
#ApiOperation(value = "Import sites in bulk")
public ResponseEntity<BulkUploadResponseDTO> uploadSitesInBulk(#RequestParam("file") MultipartFile excelFile, HttpServletRequest request){
UserPrincipal userPrincipal = (UserPrincipal) request.getAttribute("userPrincipal");
String userId = userPrincipal.getId();
log.info("Received excel file with name {}......",excelFile.getOriginalFilename());
if(!excelFile.isEmpty()){
return ResponseEntity.status(HttpStatus.CREATED).body(partnerSiteUploadService.uploadSitesInBulk(userId,excelFile));
}
else{
throw new BadRequestException("Received empty excel file");
}
}
}
while executing the test I am getting the 400 error code. the mockmvc is not calling the original API.

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

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());
}

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();

Categories