I know this question has been asked here but I can't find an answer. I have a Spring REST controller endpoint that accepts path variables but I keep getting a 404 instead of 200.
Here's my controller method:
#GetMapping("/colorNames/{colorFamily}")
public ResponseEntity<List<ColorNameFamilyDTO>> getColorNamesByColorFamily(#PathVariable String colorFamily)
{
List<ColorNameFamilyDTO> colorInformations = service.getColorNamesByColorFamily(colorFamily.toUpperCase());
return ResponseEntity.ok(colorInformations);
}
and my test is:
#RunWith(SpringRunner.class)
#WebMvcTest(InfoController.class)
public class InfoControllerTest {
#Autowired
private MockMvc mockMvc;
#MockBean
private InfoService service;
#Autowired
private ObjectMapper objectMapper;
#Test
public void testGetColorNamesByFamily() throws Exception
{
List<ColorNameFamilyDTO> colorInformations = new ArrayList<>();
Mockito.when(service.getColorNamesByColorFamily(Mockito.anyString()))
.thenReturn(colorInformations);
mockMvc.perform(get("/colorNames/{colorFamily}", "Blue")
.contentType("text/plain")).andExpect(status().isOk());
}
}
I've tried use param and also specifying the string in the path directly. What's going wrong? I'm using SpringBoot 2.1.3.RELEASE.
Adding a doPrint() shows up this on the console:
MockHttpServletRequest:
HTTP Method = GET
Request URI = /colorNames/Blue
Parameters = {}
Headers = [Content-Type:"text/plain"]
Body = <no character encoding set>
Session Attrs = {}
Handler:
Type = com.controller.Controller
Method = public org.springframework.http.ResponseEntity<java.util.List<com.dto.ColorNameFamilyDTO>> com.controller.getColorNamesByColorFamily(java.lang.String)
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 = []
If there is no endpoint for "/colorNames", you will get HTTP 404. Therefore, check your controller. Your controller class should be marked with #RestController annotation.
You can try to use RequestBuilder object to call controller
requestBuilder = MockMvcRequestBuilders.get("/colorNames/{colorFamily}", "Blue")
.contentType(MediaType.TEXT_PLAIN_VALUE)
.accept(MediaType.APPLICATION_JSON_UTF8);
mockMvc.perform(requestBuilder)
.andExpect(status().isOk())
.andDo(print())
.andReturn();
print() will state you which api path you are calling
Update :
Please check controller and test classes
#RestController
public class AddDataController {
#Autowired
public AddDataService addDataService;
#GetMapping(value = "colourNames/{pathVar}")
public ResponseEntity dataAdded(#PathVariable String pathVar){
return addDataService.printPathVariable(pathVar);
}
}
and Test class should be
#WebMvcTest(AddDataController.class)
#RunWith(SpringRunner.class)
class AddDataControllerTest {
#MockBean
public AddDataService addDataService;
#Autowired
public MockMvc mockMvc;
#BeforeEach
public void setUp(){
MockitoAnnotations.initMocks(this);
}
#Test
void dataAdded() throws Exception {
RequestBuilder requestBuilder = MockMvcRequestBuilders.get("/colourNames/{pathVar}", "Blue")
.contentType(MediaType.TEXT_PLAIN_VALUE);
mockMvc.perform(requestBuilder)
.andExpect(status().isOk())
.andDo(print())
.andReturn();
}
}
Can you share print() response here
Related
If I try to test the post() endpoint, I see:
java.lang.AssertionError: No value at JSON path "$.firstName"
Caused by: java.lang.IllegalArgumentException: json can not be null or empty
But with the test for the get() all work fine.
And in the postTest() the result for status is correct.
Where is my mistaker?
Is it correct way to test the rest controller in this style?
#RunWith(MockitoJUnitRunner.Silent.class)
public class Temp {
private final Employee successfullyRegisteredEmployee = new Employee(2L, "Iven");
private final Employee employeeGetById = new Employee(2L, "Iven");
#Mock
private EmployeeServiceImpl serviceMock;
private MockMvc mockMvc;
#Before
public void setUp() throws Exception {
mockMvc = MockMvcBuilders.standaloneSetup(new EmployeeControllerImpl( serviceMock))
.build();
}
#Test
public void getTest() throws Exception {
when(serviceMock.getEmployee(2L)).thenReturn(employeeGetById);
mockMvc.perform(get("/employee/get/2"))
.andExpect(status().is(200))
.andExpect(content().json(("{'firstName':'Iven'}")));
verify(serviceMock).getEmployee(2L);
}
#Test
public void postTest() throws Exception {
String json = "{\n" +
" \"firstName\": \"Iven\"\n"
"}";
when(serviceMock.register(employeeForRegister)).thenReturn(successfullyRegisteredEmployee);
mockMvc.perform( MockMvcRequestBuilders
.post("/employee/register")
.content(json)
.contentType(MediaType.APPLICATION_JSON)
.accept(MediaType.APPLICATION_JSON))
.andExpect(status().is(201))
.andExpect(jsonPath("$.firstName", Matchers.is("Iven")));
}
}
#RestController
#RequestMapping("/employee")
public class EmployeeControllerImpl implements EmployeeController {
private final EmployeeService service;
public EmployeeControllerImpl(EmployeeService service) {
this.service = service;
}
#PostMapping(path = "/register",
consumes = "application/json",
produces = "application/json"
)
public ResponseEntity<Employee> registerEmployee(#Valid #RequestBody Employee employee) {
Employee registeredEmployee = service.register(employee);
return ResponseEntity.status(201).body(registeredEmployee);
}
}
Seems like problem could be with when(serviceMock.register(employeeForRegister)).thenReturn(successfullyRegisteredEmployee);.
Did you try to have breakpoint on return ResponseEntity.status(201).body(registeredEmployee); to check if registeredEmployee is actually filled?
If it's empty then try replacing mock with when(serviceMock.register(any())).thenReturn(successfullyRegisteredEmployee); and if it works that means either equals() method is not overridden for Employee or comparison just fails.
I want to test controller of Spring boot API with class structure as below:
Controller:
#RestController
#RequestMapping("/member-management")
#Validated
public class MemberManagementController {
private final MemberManagementService memberManagementService;
public MemberManagementController(MemberManagementService memberManagementService) {
this.memberManagementService = memberManagementService;
}
#GetMapping(value = "/view-member")
public ResponseEntity<?> viewMember(
#NotBlank(message = "username must not be blank!!")
#Size(max = 20, message = "maximum size of username id is 20!!")
#RequestParam("username") String username) {
...
}
...
Controller advice:
#ControllerAdvice
public class CustomRestExceptionHandler extends ResponseEntityExceptionHandler {
#ExceptionHandler(ConstraintViolationException.class)
public ResponseEntity<Object> handleConstaintViolatoinException(final ConstraintViolationException ex) {
List<String> details = new ArrayList<>();
Set<ConstraintViolation<?>> violations = ex.getConstraintViolations();
for (ConstraintViolation<?> violation : violations) {
details.add(violation.getMessage());
}
ApiErrorResUtil error = new ApiErrorResUtil(String.valueOf(HttpStatus.BAD_REQUEST.value()),
"Request param error", details);
return new ResponseEntity<>(error, HttpStatus.BAD_REQUEST);
}
...
}
Unit test:
#RunWith(MockitoJUnitRunner.class)
public class MemberManagementControllerTest {
#InjectMocks
private MemberManagementController memberManagementController;
#Mock
private MemberManagementService memberManagementService;
private MockMvc mockMvc;
#Before // Execute before each test method
public void setup() {
this.mockMvc = MockMvcBuilders.standaloneSetup(memberManagementController)
.setControllerAdvice(new CustomRestExceptionHandler()) // add ControllerAdvice to controller test
.build();
}
#Test
public void viewMember_usernameSizeExceedsMaximumLimit() throws Exception {
// Value from client
String username = "a12345678901234567890"; // 21 characters
MemberResDtoDataDummy memberResDtoDataDummy = new MemberResDtoDataDummy();
when(memberManagementService.viewMember(username)).thenReturn(memberResDtoDataDummy.getMember1());
mockMvc.perform(get("/member-management/view-member").param("username", username))
.andExpect(status().isBadRequest()).andReturn();
}
Problem:
java.lang.AssertionError: Status expected:<400> but was:<200>
at org.springframework.test.util.AssertionErrors.fail(AssertionErrors.java:59)
...
Could anybody help me to resolve this proplem, why expected status is 200 instead of 400, other tests of POST, PUT, PATCH, DELETE request method with invalid inputted param are still working fine :(
When you want to test your UI layer without the cost of starting a server, you have to define this test as a spring-boot one and autowired the MockMvc.
#SpringBootTest
#AutoConfigureMockMvc
This class-annotations will load all the applicationContext without server.
If you just want to load your web layer, put just this annotation on your test class.
#WebMvcTest
With this annotation Spring Boot instantiates only the web layer rather than the whole context.
In both case you have to autowired the MockMvc type.
Hi I'm trying to implement junit in my controller. But what I get is 201 instead of 200.
Below is my controller
#RestController
#RequestMapping(value = "/treat")
public class TreatController {
private final TreatService treatService;
#Autowired
public TreatController(TreatService treatService){
this.treatService = treatService;
}
#PostMapping
public ResponseEntity<CommonResponse> addNew(
#RequestBody Treat treat) throws RecordNotFoundException{
CommonResponse response = new CommonResponse();
response.setStatus(CommonConstants.OK);
response.setData(treatService.save(treat));
return new ResponseEntity<>(response, HttpStatus.CREATED);
}
}
next is my Junit testing:
#RunWith(SpringJUnit4ClassRunner.class)
#WebMvcTest(TreatController.class)
public class TreatControllerTest {
private RecordNotFoundException recordException = new RecordNotFoundException("");
private final String title = "{\"title\" : \"title\"}";
#Autowired
private MockMvc mockMvc;
#MockBean
private TreatService treatService;
#Test
public void addNew() throws Exception{
Treatment treatment = new Treatment();
given(treatmentService.save(
Mockito.any(Treat.class))).willReturn(treat);
mockMvc.perform(post("/treats")
.content(title)
.accept(MediaType.APPLICATION_JSON_VALUE)
.contentType(MediaType.APPLICATION_JSON_VALUE))
.andDo(print())
.andExpect(status().isOk());
Mockito.verify(treatService).save(Mockito.any(Treat.class));
}
}
is there anything that I missed?
By the way, I dont use Json. I just inserted it because it works.
That's what you return.
return new ResponseEntity<>(response, HttpStatus.CREATED);
HttpStatus.CREATED returns 201 and indicates that a resource has been created by the request
How ever in your testcase you are expecting OK(200) .andExpect(status().isOk());
According to HTTP1.1/ specs Post request should always result in the creation of a resource. So it makes sense to return 201 from there. All your need to do is change your testcase assertion expected value to HTTPStatus.CREATED.
While trying to get my PUT MockMvc test to work, I found that JSON was not supported. Using an answer from this thread:
Configure MappingJacksonHttpMessageConverter
I was able to resolve this by extending the WebMvcConfigurationSupport class. However, when I use this override, it appears as though the ModelAndView data returned in my GET tests is now NULL. I know I can get the response data using this:
String content = result.getResponse().getContentAsString();
But, does anyone have an explanation for why the ModelAndView data is NULL?
Here is my MockMVC test class:
#RunWith(SpringJUnit4ClassRunner.class)
#WebAppConfiguration
#ContextConfiguration("test-rest-context.xml")
public class AccountControllerTest {
#Autowired
private WebApplicationContext webApplicationContext;
private MockMvc mockMvc;
private MediaType contentType = new MediaType(MediaType.APPLICATION_JSON.getType(),
MediaType.APPLICATION_JSON.getSubtype(),
Charset.forName("utf8"));
#Before
public void setUp() throws Exception {
this.mockMvc = MockMvcBuilders.webAppContextSetup(this.webApplicationContext).build();
}
#Test
public void findCustomerByID() throws Exception {
MvcResult result = mockMvc.perform(get("/api/account/customers/{customerID}", "123").accept(contentType)
.param("fields", "id", "email", "userName")
.contentType(contentType))
.andExpect(status().isOk())
.andReturn();
ModelAndView mav = result.getModelAndView();
// mav is NULL here after I extend WebMvcConfigurationSupport class.
}
}
I am writing a test for controller using spring test mvc framework. I am trying to test a post request and it produces JSON. The actual code is running but test is failing.
I an getting the error Status expected 200 but getting 415 which means unsupported media type. i check all examples on net and it seems my implementation is correct. please help me out.
MyTestcase TestStringPost() is running successfully but TestJSONPost is failing.
I am using Spring 3.2
My Controller
#Controller
public class HomeController {
#RequestMapping(value = "/v1/checkstringvalue", method = RequestMethod.POST, produces = "application/json")
public #ResponseBody void testStringPost(#RequestBody String value) {
logger.info("Welcome home! The client locale is {}.");
}
#RequestMapping(value = "/v1/checkjsonvalue", method = RequestMethod.POST, produces = "application/json")
public #ResponseBody String testJSONPost(#RequestBody Map<String, String> userMap) {
System.out.println("Inside test jsonpost controller");
logger.info("Welcome home! The client locale is {}.");
return "Success";
}
}
This is my Test Controller
#RunWith(SpringJUnit4ClassRunner.class)
#ContextConfiguration
#WebAppConfiguration
public class HomeControllerTest {
#Autowired
private WebApplicationContext wac;
private MockMvc mockMvc;
private static final String PROVIDER_A = "PROVIDER_A";
public static final MediaType APPLICATION_JSON_UTF8 = new MediaType(
MediaType.APPLICATION_JSON.getType(),
MediaType.APPLICATION_JSON.getSubtype(), Charset.forName("utf8"));
#Configuration
public static class TestConfiguration {
#Bean
public HomeController simpleController() {
return new HomeController();
}
}
#Before
public void setup() {
mockMvc = MockMvcBuilders.webAppContextSetup(wac).build();
}
#Test
public void testStringPost() throws Exception {
final String createJson = "hello";
ResultActions resultActions = mockMvc.perform(post(
"/v1/checkstringvalue").contentType(MediaType.TEXT_PLAIN)
.content(createJson));
resultActions.andDo(print());
resultActions.andExpect(status().isOk());
}
#Test
public void testjsonPost() throws Exception {
System.out.println("Inside testjsonpost");
final String createJson = "{\"name\":\"hello\",\"email\":\"hello#example.com\"}";
ObjectMapper objectMapper = new ObjectMapper();
ObjectNode userJson = objectMapper.createObjectNode();
userJson.put("name", "tarun");
userJson.put("email", "tarun#gmail.com");
ResultActions resultActions = mockMvc
.perform(post("/v1/checkjsonvalue")
.contentType(APPLICATION_JSON_UTF8)
.accept(APPLICATION_JSON_UTF8)
.content(objectMapper.writeValueAsBytes(userJson)));
resultActions.andDo(print());
resultActions.andExpect(status().isOk());
}
}
Ok, so having the
perform(post("/myapi").contentType(MediaType.APPLICATION_JSON).content("{\"mykey\":\"myvalue\"}"))
is important.
BUT also check your controller: if it is missing any of these annotations, it won't work:
#EnableWebMvc
#Controller
public class MyController {
#RequestMapping(value="/myapi", method=RequestMethod.POST)
public void postOnApi(#RequestBody MyWidget myWidget) {
}
}
The #EnableWebMvc #Controller and #RequestBody are all needed (in my experimenting) to remove the 415 status code error.
Your test code:
ResultActions resultActions = mockMvc
.perform(post("/v1/checkjsonvalue")
.contentType(APPLICATION_JSON_UTF8)
.accept(APPLICATION_JSON_UTF8)
.content(objectMapper.writeValueAsBytes(userJson)));
Seems to add the contentType() header, but your source code does not have a consumes attribute in the #RequestMapping
#RequestMapping(value = "/v1/checkjsonvalue", method = RequestMethod.POST, produces = "application/json")
public #ResponseBody String testJSONPost(#RequestBody Map<String, String> userMap) {
System.out.println("Inside test jsonpost controller");
logger.info("Welcome home! The client locale is {}.");
return "Success";
}
I think that if you take out the contentType() in your test case or add a consumes in the source code then it might work.
if you are using xml based config for your spring mvc project make sure that you have put #EnableWebMvc on your controller.