URL : http://X.X.X.X/APPLICATION/abc?a=1
Is my URL correct?
JAVA Code:
#RequestMapping(value = "/abc?", method = RequestMethod.GET)
#ResponseBody
public ModelAndView page (#RequestParam(value = "a") String a) {
...
Code Logic
...
}
Am I handling it correctly?
Remove the question mark after abc.
value = "/abc"
this is how I am handling the url params. I have also attached the test code below to make it easier to understand.
Here's my controller
package com.example.demo;
import org.springframework.web.bind.annotation.*;
#RestController
#RequestMapping("/APPLICATION")
public class DemoController {
#GetMapping("/abc")
public #ResponseBody String getAbc(#RequestParam String a) {
return a;
}
}
And here's the test code
package com.example.demo;
import org.junit.jupiter.api.Test;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.autoconfigure.web.servlet.AutoConfigureMockMvc;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.test.web.servlet.MockMvc;
import static org.hamcrest.Matchers.equalTo;
import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.get;
import static org.springframework.test.web.servlet.result.MockMvcResultHandlers.print;
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.content;
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status;
#SpringBootTest
#AutoConfigureMockMvc
class DemoApplicationTests {
#Autowired
private MockMvc mockMvc;
#Test
void contextLoads() {
}
#Test
public void shouldGetParamFromUrl() throws Exception {
this.mockMvc.perform(get("/APPLICATION/abc?a=1"))
.andDo(print()).andExpect(status().isOk())
.andExpect(content().string(equalTo("1")));
this.mockMvc.perform(get("/APPLICATION/abc?a=99"))
.andDo(print()).andExpect(status().isOk())
.andExpect(content().string(equalTo("99")));
}
}
Related
I am new in springboot. I am just watching the Spring in Action and programming follow the author.
then things get difficult when i just reading the chapter 1. I need to test a controller. the code in this book is:
package tacos;
import static org.hamcrest.Matchers.containsString;
import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.get;
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.content;
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status;
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.view;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.autoconfigure.web.servlet.WebMvcTest;
import org.springframework.test.context.junit4.SpringRunner;
import org.springframework.test.web.servlet.MockMvc;
#RunWith(SpringRunner.class)
#WebMvcTest(HomeController.class) // <1>
public class HomeControllerTest {
#Autowired
private MockMvc mockMvc; // <2>
#Test
public void testHomePage() throws Exception {
mockMvc.perform(get("/")) // <3>
.andExpect(status().isOk()) // <4>
.andExpect(view().name("home")) // <5>
.andExpect(content().string( // <6>
containsString("Welcome to...")));
}
}
I am using springboot 2.7.3 so i just remove #RunWith(SpringRunner.class) from my code. and i immediately get an error when do the test:
after google that i realized it might not find my controller so I add #Import(HomeController.class) to the test class. It looks like:
package com.qph.tacos;
import org.hamcrest.Matchers;
import org.junit.jupiter.api.Test;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.autoconfigure.web.servlet.WebMvcTest;
import org.springframework.context.annotation.Import;
import org.springframework.test.web.servlet.MockMvc;
import org.springframework.test.web.servlet.request.MockMvcRequestBuilders;
import org.springframework.test.web.servlet.result.MockMvcResultMatchers;
#WebMvcTest(HomeControllerTest.class)
#Import(HomeController.class)
public class HomeControllerTest {
#Autowired
private MockMvc mockMvc;
#Test
public void testHomePage() throws Exception {
mockMvc.perform(MockMvcRequestBuilders.get("/"))
.andExpect(MockMvcResultMatchers.status().isOk())
.andExpect(MockMvcResultMatchers.view().name("home"))
.andExpect(MockMvcResultMatchers.content().string(Matchers.containsString("Welcome to...")));
}
}
then it just pass the test!!!
now i just wonder why the code written by the author can pass the test without #Import(HomeController.class)?
my directory structure is like:
the controller to be tested is:
package com.qph.tacos;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.GetMapping;
#Controller
public class HomeController {
#GetMapping("/")
public String home() {
// return the name of template
return "home";
}
}
I am new in springboot. maybe it's a simple question but i really spent so much time on it.
Thanks for your help!
Change to
#WebMvcTest(HomeController.class)
You should reference the controller under test with this annotation, otherwise it won't be loaded into the application context.
In Spring boot framework, I'm finding a difficulty with the controller Unit testing using JUnit and Mockito. I want to test this method. How to test DELETE Request method:
// delete application
Controller class
#DeleteMapping("/applications")
public String deleteApplicationByObject(#RequestBody Application application) {
applicationService.deleteById(application.getId());
return "Deleted";
}
// delete application
Service class
#Override
#Transactional
public String removeById(Long id) {
dao.deleteById(id);
return "SUCCESS";
}
// delete application
Dao class
#Override
public void deleteById(Long id) {
Application application = findById(id);
em.remove(application);
}
Thank you in advance.
After a while i'm able to find a solution of my question which is,
ApplicationControllerTest.class
package com.spring.addapplication.test.controller;
import static org.mockito.MockitoAnnotations.initMocks;
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.content;
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status;
import java.util.ArrayList;
import java.util.List;
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.mockito.Mock;
import org.mockito.Mockito;
import org.springframework.http.MediaType;
import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;
import org.springframework.test.web.servlet.MockMvc;
import org.springframework.test.web.servlet.request.MockMvcRequestBuilders;
import org.springframework.test.web.servlet.setup.MockMvcBuilders;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.spring.addapplication.controller.ApplicationController;
import com.spring.addapplication.model.Application;
import com.spring.addapplication.service.ApplicationService;
import com.spring.addapplication.url.UrlChecker;
#RunWith(SpringJUnit4ClassRunner.class)
public class ApplicationControllerTest {
#Mock
ApplicationService applicationService;
private MockMvc mockMvc;
#Before
public void setUp() throws Exception {
initMocks(this);// this is needed for inititalization of mocks, if you use #Mock
ApplicationController controller = new ApplicationController(applicationService,urlChecker);
mockMvc = MockMvcBuilders.standaloneSetup(controller).build();
}
#Test
public void deleteApplication() throws Exception {
Mockito.when(applicationService.removeById(10001L)).thenReturn("SUCCESS");
mockMvc.perform(MockMvcRequestBuilders.delete("/applications", 10001L))
.andExpect(status().isOk());
}
I am having issues creating a unit test for my LinuxCommandController. The LinuxCommandController returns a string from the responsebody of it's post mapping. It invokes LinuxCommandService which runs a command using the JSON string from the post body.
I've tried numerous ways to get this to work, my latest error is below. I've tried having a string returned as well, and get all types of errors. I feel like this shouldn't be this difficult, and I'm missing something simple.
Any help would be appreciated.
caservice: Compilation failure
[ERROR] /api/LinuxControllerTest.java:[59,55] cannot find symbol
[ERROR] symbol: method thenReturn(org.springframework.http.ResponseEntity)
[ERROR] location: class java.lang.String
Here is my junit test class.
package com.development.api;
import com.development.services.LinuxCommandService;
//import jdk.internal.net.http.ResponseBodyHandlers;
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.mockito.InjectMocks;
import org.mockito.MockitoAnnotations;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.autoconfigure.web.servlet.AutoConfigureMockMvc;
import org.springframework.boot.test.autoconfigure.web.servlet.WebMvcTest;
import org.springframework.boot.test.mock.mockito.MockBean;
import org.springframework.http.HttpStatus;
//import org.springframework.web.bind.annotation.ResponseBody;
import org.springframework.http.ResponseEntity;
import org.springframework.security.test.context.support.WithMockUser;
import org.springframework.test.context.junit4.SpringRunner;
import org.springframework.test.web.servlet.MockMvc;
import org.springframework.test.web.servlet.request.MockMvcRequestBuilders;
import org.springframework.test.web.servlet.result.MockMvcResultMatchers;
import org.springframework.test.web.servlet.setup.MockMvcBuilders;
import org.springframework.http.MediaType;
import static org.mockito.Mockito.when;
#RunWith(SpringRunner.class)
#WebMvcTest(LinuxController.class)
#WithMockUser
#AutoConfigureMockMvc
public class LinuxControllerTest {
#Autowired
private MockMvc mockMvc;
#MockBean
private LinuxCommandService linuxCommandService;
#InjectMocks
private LinuxController linuxController;
#Before
public void setUp() {
MockitoAnnotations.initMocks(this);
mockMvc = MockMvcBuilders
.standaloneSetup(linuxController)
.build();
}
#Test
public void test_that_the_jenkins_controller_received_all_required_params_and_our_service_returned_a_201_created() throws Exception {
String jsonValue = "{\"command\":\"CMD12}";
ResponseEntity linuxResponse = ResponseEntity.status(HttpStatus.CREATED)
.body(HttpStatus.CREATED);
when(linuxCommandService.runCommand(jsonValue).thenReturn(linuxResponse));
mockMvc.perform( MockMvcRequestBuilders
.post("/api/linux/command")
.content(jsonValue)
.contentType(MediaType.APPLICATION_JSON))
.andExpect(MockMvcResultMatchers.status().isCreated());
}
}
Here is the controller.
import com.services.LinuxCommandService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.http.HttpStatus;
import org.springframework.web.bind.annotation.*;
import lombok.NoArgsConstructor;
import javax.validation.constraints.NotBlank;
#RestController
#NoArgsConstructor
#RequestMapping("/api/linux")
public class LinuxController {
#Autowired
LinuxCommandService linuxCommandService;
#PostMapping(path = "/command", consumes = "application/json", produces = "application/json")
#ResponseStatus(code = HttpStatus.CREATED)
public String create(#RequestBody #NotBlank String requestCommand) {
return linuxCommandService.runCommand(requestCommand);
}
}
And the service method returns a string,
public String runCommand(String requestCommand) {
.....
return "string returned";
}
when(linuxCommandService.runCommand(jsonValue))
.thenReturn(linuxResponse.toString());
I have a problem when trying to test the JSON output from a Spring REST Service using MockMvcResultMatchers where the returned object should contain a Long value.
The test will only pass when the value within the JSON object is is higher than Integer.MAX_VALUE. This seems a little odd to me as I feel that I should be able to test the full range of applicable values.
I understand that since JSON does not include type information it is performing a best guess at the type at de-serialisation, but I would have expected there to be a way to force the type for extraction when performing the comparison in the MockMvcResultMatchers.
Full code is below but the Test is:
#Test
public void testGetObjectWithLong() throws Exception {
Long id = 45l;
ObjectWithLong objWithLong = new ObjectWithLong(id);
Mockito.when(service.getObjectWithLong(String.valueOf(id))).thenReturn(objWithLong);
mockMvc.perform(MockMvcRequestBuilders.get("/Test/" + id))
.andExpect(MockMvcResultMatchers.status().isOk())
.andExpect(MockMvcResultMatchers.jsonPath("$longvalue")
.value(Matchers.isA(Long.class)))
.andExpect(MockMvcResultMatchers.jsonPath("$longvalue")
.value(Matchers.equalTo(id)));
}
and the Result is:
java.lang.AssertionError: JSON path$longvalue
Expected: is an instance of java.lang.Long
but: <45> is a java.lang.Integer
at org.springframework.test.util.MatcherAssertionErrors.assertThat(MatcherAssertionErrors.java:80)
...
Any ideas or suggestions as to the proper way to fix this would be appreciated. Obviously I could just add Integer.MAX_VALUE to the id field in the test but that seems fragile.
Thanks in advance.
The following should be self contained apart from the third party libraries
import org.hamcrest.Matchers;
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.mockito.InjectMocks;
import org.mockito.Mock;
import org.mockito.Mockito;
import org.mockito.MockitoAnnotations;
import org.mockito.runners.MockitoJUnitRunner;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.http.converter.json.MappingJackson2HttpMessageConverter;
import org.springframework.stereotype.Service;
import org.springframework.test.web.servlet.MockMvc;
import org.springframework.test.web.servlet.request.MockMvcRequestBuilders;
import org.springframework.test.web.servlet.result.MockMvcResultMatchers;
import org.springframework.test.web.servlet.setup.MockMvcBuilders;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
import org.springframework.web.bind.annotation.RestController;
#RunWith(MockitoJUnitRunner.class)
public class TestControllerTest {
private MockMvc mockMvc;
#Mock
private RandomService service;
#InjectMocks
private TestController controller = new TestController();
#Before
public void setUp() {
MockitoAnnotations.initMocks(this);
mockMvc = MockMvcBuilders.standaloneSetup(controller)
.setMessageConverters(new MappingJackson2HttpMessageConverter())
.build();
}
#Test
public void testGetObjectWithLong() throws Exception {
Long id = 45l;
ObjectWithLong objWithLong = new ObjectWithLong(id);
Mockito.when(service.getObjectWithLong(String.valueOf(id))).thenReturn(objWithLong);
mockMvc.perform(MockMvcRequestBuilders.get("/Test/" + id))
.andExpect(MockMvcResultMatchers.status().isOk())
.andExpect(MockMvcResultMatchers.jsonPath("$longvalue").value(Matchers.isA(Long.class)))
.andExpect(MockMvcResultMatchers.jsonPath("$longvalue").value(Matchers.equalTo(id)));
}
#RestController
#RequestMapping(value = "/Test")
private class TestController {
#Autowired
private RandomService service;
#RequestMapping(value = "/{id}", method = RequestMethod.GET)
public ObjectWithLong getObjectWithLong(#PathVariable final String id) {
return service.getObjectWithLong(id);
}
}
#Service
private class RandomService {
public ObjectWithLong getObjectWithLong(String id) {
return new ObjectWithLong(Long.valueOf(id));
}
}
private class ObjectWithLong {
private Long longvalue;
public ObjectWithLong(final Long theValue) {
this.longvalue = theValue;
}
public Long getLongvalue() {
return longvalue;
}
public void setLongvalue(Long longvalue) {
this.longvalue = longvalue;
}
}
}
You can use anyOf Matcher along with a Class match against the Number super class and set it up like
.andExpect(MockMvcResultMatchers.jsonPath("$longvalue")
.value(Matchers.isA(Number.class)))
.andExpect(MockMvcResultMatchers.jsonPath("$longvalue")
.value(Matchers.anyOf(
Matchers.equalTo((Number) id),
Matchers.equalTo((Number) id.intValue()))));
I have a simple PersonController class that provides save() method to persist the object from http post request.
package org.rw.controller;
import java.sql.Timestamp;
import java.util.List;
import org.rw.entity.Person;
import org.rw.service.PersonService;
import org.rw.spring.propertyeditor.TimestampPropertyEditor;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Controller;
import org.springframework.ui.Model;
import org.springframework.ui.ModelMap;
import org.springframework.web.bind.WebDataBinder;
import org.springframework.web.bind.annotation.InitBinder;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
#Controller
#RequestMapping(value="/person")
public class PersonController {
private static final Logger logger = LoggerFactory.getLogger(PersonController.class);
#Autowired
private PersonService personService;
#Autowired
TimestampPropertyEditor timestampPropertyEditor;
#InitBinder
public void initBinder(WebDataBinder binder) {
binder.registerCustomEditor(Timestamp.class, "dob", timestampPropertyEditor);
}
#RequestMapping(value="/save", method=RequestMethod.POST)
public String save(Model model, Person person) {
Long personId = personService.save(person);
return "redirect:view/" + personId;
}
}
As the save() method returns as return "redirect:view/" + personId;. It will be diffrerent for every request. it may be like "view/5" or "view/6" depending on the id of the object that has been persisted.
Then i have a simple class to test the above controller with spring mocking.
package org.rw.controller;
import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.get;
import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.post;
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.redirectedUrl;
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status;
import org.junit.Test;
import org.rw.service.UserService;
import org.rw.test.SpringControllerTest;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
import org.springframework.security.core.Authentication;
import org.springframework.security.core.context.SecurityContextHolder;
import org.springframework.security.core.userdetails.UserDetails;
public class PersonControllerTest extends SpringControllerTest {
#Autowired
private UserService userService;
#Test
public void add() throws Exception {
mockMvc.perform(get("/person/add", new Object[0])).andExpect(status().isOk());
}
#Test
public void save() throws Exception {
UserDetails userDetails = userService.findByUsername("anil");
Authentication authToken = new UsernamePasswordAuthenticationToken (userDetails.getUsername(), userDetails.getPassword(), userDetails.getAuthorities());
SecurityContextHolder.getContext().setAuthentication(authToken);
mockMvc.perform(
post("/person/save", new Object[0])
.param("firstName", "JunitFN")
.param("lastName", "JunitLN")
.param("gender", "M")
.param("dob", "11/02/1989")
).andExpect(
redirectedUrl("view")
);
}
}
now here i have a problem that redirectedUrl("view") is rejecting value "view/5". I have tried redirectedUrl("view*") and redirectedUrl("view/*") but its not working.
Edit :
Here I have got a workaround as per below
MvcResult result = mockMvc.perform(
post("/person/save", new Object[0])
.param("firstName", "JunitFN")
.param("lastName", "JunitLN")
.param("gender", "MALE")
.param("dob", "11/02/1989")
).andExpect(
//redirectedUrl("view")
status().isMovedTemporarily()
).andReturn();
MockHttpServletResponse response = result.getResponse();
String location = response.getHeader("Location");
Pattern pattern = Pattern.compile("\\Aview/[0-9]+\\z");
assertTrue(pattern.matcher(location).find());
but still i am looking for the proper way.
update:
I have posted the same issue on spring jira here :
Since spring 4.0 you can use redirectedUrlPattern as pointed by Paulius Matulionis
As of spring 3.x this is not supported out of the box but you can easily add you custom result matcher
private static ResultMatcher redirectedUrlPattern(final String expectedUrlPattern) {
return new ResultMatcher() {
public void match(MvcResult result) {
Pattern pattern = Pattern.compile("\\A" + expectedUrlPattern + "\\z");
assertTrue(pattern.matcher(result.getResponse().getRedirectedUrl()).find());
}
};
}
And use it like build-in matcher
mockMvc.perform(
post("/person/save", new Object[0])
.param("firstName", "JunitFN")
.param("lastName", "JunitLN")
.param("gender", "M")
.param("dob", "11/02/1989")
).andExpect(
redirectedUrlPattern("view/[0-9]+")
);
Since 4.0 it is available in Spring itself.
Please check here.