How to partially mock HttpServletRequest using Mockito - java

i am mocking a HttpServletRequest , in servlet call there are new values getting set in request because using same request we are dispaching request to some jsp so request object is used as a input object to servlet as well as output for next page.
i mocked all input parameters , but for all request.setAttribute() , my code is doing nothing as it's a mocked class , say if i have
request.setAttribute(a,"10")
System.out.println("a = " + request.getAttribute("a"));
i get null cuz i haven't given any behavious for Request.getAttribute("a") , and i can't , it's my response for next page , so that explain i need 2 behaviour my request object thus partial mocking , and i am unable to spy or do any partial mocking on it so far. any ideas?
Code :
//Testcase
Myservlet.java
public void doPost(request,response)
{
String a = request.getAttribute("a");
String b = request.getAttribute("b");
int sum = Integer.parseInt(a) + Integer.parseInt(b);
request.setAttribute("sum",sum);
//well in this example i can use sum what i calculated but in real senario i can't , i have to use request.getAttribute("sum")
insertSumIntoDB(request.getAttribute("sum"));
}
}
//testMyservlet.java
#test
public void testServlet()
{
HttpServletRequest request = mock(HttpServletRequest.class);
HttpServletResponse response = mock(HttpServletResponse.class);
when(request.getAttribute(a)).thenReturn("10");
when(request.getAttribute(b)).thenReturn("20");
new Myservlet(request,response);
}

Spring's MockHttpServletRequest and MockHttpServletResponse are useful for that purpose. E.g.
MockHttpServletRequest request = new MockHttpServletRequest();
MockHttpServletResponse response = new MockHttpServletResponse();
request.addHeader(HttpHeaders.HOST, "myhost.com");
request.setLocalPort(PORT_VALID); // e.g. 8081
request.setRemoteAddr(REQUEST_IP); // e.g. 127.0.0.1
then I can call myclass.method(request, response, ...) and check whether some attribute has been correctly set into the request, e.g.
MyBean attr = (MyBean) request.getAttribute(ATTRIBUTE_NAME));
// do my Assert.* stuff with 'attr'
MockHttpServletRequest and MockHttpServletResponse work fine where mock(HttpServletRequest.class) fails, for instance where you need to get back the real content of a request attribute which has been previously set within your business logic.

You need to store attributes into a collection :
// Collection to store attributes keys/values
final Map<String, Object> attributes = new ConcurrentHashMap<String, Object>();
// Mock setAttribute
Mockito.doAnswer(new Answer<Void>() {
#Override
public Void answer(InvocationOnMock invocation) throws Throwable {
String key = invocation.getArgumentAt(0, String.class);
Object value = invocation.getArgumentAt(1, Object.class);
attributes.put(key, value);
System.out.println("put attribute key="+key+", value="+value);
return null;
}
}).when(request).setAttribute(Mockito.anyString(), Mockito.anyObject());
// Mock getAttribute
Mockito.doAnswer(new Answer<Object>() {
#Override
public Object answer(InvocationOnMock invocation) throws Throwable {
String key = invocation.getArgumentAt(0, String.class);
Object value = attributes.get(key);
System.out.println("get attribute value for key="+key+" : "+value);
return value;
}
}).when(request).getAttribute(Mockito.anyString());

Mockito supports real partial mocks: Real partial mocks (Since 1.8.0)
I think it fits your needs

Related

Get request header in spring boot

How do I get the header and body of the current request from an application which called my Springboot application? I need to extract this information. Unfortunately this does not work. I tried to get the current request with this code sample (https://stackoverflow.com/a/26323545/5762515):
public static HttpServletRequest getCurrentHttpRequest(){
RequestAttributes requestAttributes = RequestContextHolder.getRequestAttributes();
if (requestAttributes instanceof ServletRequestAttributes) {
HttpServletRequest request = ((ServletRequestAttributes)requestAttributes).getRequest();
return request;
}
throw new IllegalArgumentException("Request must not be null!");
}
And then I tried to get the body
ContentCachingRequestWrapper requestWrapper = (ContentCachingRequestWrapper) currentRequest;
String requestBody = new String(requestWrapper.getContentAsByteArray());
Can someone tell me what im doing wrong?
Thanks in advance
#RestController
public class SampleController {
#PostMapping("/RestEndpoint")
public ResponseEntity<?> sampleEndpoint(#RequestHeader Map<String, String> headers,#RequestBody Map<String,String> body) {
//Do something with header / body
return null;
}
}
If the application's are communicating through a rest endpoint I believe this would be the simplest solution. In spring you can add RequestHeader and RequestBody annotations to method arguments to have them setup to be used.
Of course you can map RequestBody directly to some POJO instead of using a map but just as an example.
Let me know if this is what you were looking for !
#TryHard, You're using spring boot then following way is more preferable for you,
#RestController
public class SampleController {
#RequestMapping("/get-header-data")
public ResponseEntity<?> sampleEndpoint(HttpServletRequest request) {
// request object comes with various in-built methods use as per your requirement.
request.getHeader("<key>");
}
}
you can get header with your code but need apply some changes.
private String getRequest() throws Exception {
RequestAttributes attribs = RequestContextHolder.getRequestAttributes();
if (attribs != null) {
HttpServletRequest request = ((ServletRequestAttributes) attribs).getRequest();
return request ;
}
throw new IllegalArgumentException("Request must not be null!");
}
after you can extract header info from request. For example if you want get Accept-Encoding
String headerEncoding = getRequest().getHeader("Accept-Encoding");
obliviusly you don't use this approce if not necessary.
If you want exract the body NOT use this solution

Spring Boot unittest with WebRequest with form submission included

I am writing unittests and I've stumbled across something I can't find a solution for that fits my needs or code I already have.
The User first comes at a page where they have to choose (from a dropdown list) what brand they want to make a configuration for. After they click 'submit', it takes them to a page where all the appropriate settings are listed per category.
Now, the choosing of the brand is a form and it's submitted to this method:
// Display a form to make a new Configuration
#PostMapping("/addConfig")
public String showConfigurationForm(WebRequest request, Model model) {
// Get the ID of the selected brand
Map<String, String[]> inputMap = request.getParameterMap();
for (Entry<String, String[]> input : inputMap.entrySet()) {
if (input.getValue().length > 0
&& input.getKey().startsWith("brand")) {
brandId = Integer.parseInt(input.getValue()[0]);
}
}
// Load the view
model.addAttribute("categoryResult",
databaseService.getCategories(brandId));
model.addAttribute("configItemsMap",
databaseService.getAddConfigItems(brandId));
return "addConfig";
}
I want to unittest this method to see if the model has the attributes we expect it to.
This is the unit test I have now:
#RunWith(SpringRunner.class)
#SpringBootTest(webEnvironment = WebEnvironment.RANDOM_PORT)
#ActiveProfiles("test")
public class AddConfigurationTest {
#Autowired
AddConfigurationController addConfigurationController;
#MockBean
DatabaseService databaseServiceTest;
#Mock
WebRequest webRequest;
#Before
public void setup() {
// Make Categories
List<ItemCategory> defaultCategories = new ArrayList<>();
defaultCategories.add(new ItemCategory(1, 1, "GPS settings"));
// Mock it
Mockito.when(this.databaseServiceTest.getCategories(1)).thenReturn(
defaultCategories);
}
#Test
public void configurationFormShouldContainCategories() {
// TODO: Still needs param for webrequest
// Make a model
Model model = new ExtendedModelMap();
addConfigurationController.showConfigurationForm(webRequest, model);
// Get the list from the model
#SuppressWarnings("unchecked")
List<ItemCategory> categoryList = (List<ItemCategory>) model.asMap()
.get("categoryResult");
System.out.println(categoryList);
}
}
The System.out.println now outputs: []
I am sure it has to do with the WebRequest, because as I have it now this WebRequest does not have the input from a form the showConfigurationForm method needs.
My question is: how can I add the right data to WebRequest so the test will return a List? Or is there another way around that I have not figured out?
Just configure your Mock WebRequest object before executing the test:
#Before
public void setup()
{
Map<String, String[]> mockParameterMap = new HashMap<>();
mockParameterMap.put("brand00", new String[]{"value01"});
// add all the parameters you want ...
Mockito.when(webRequest.getParameterMap())
.thenReturn(mockParameterMap);
}
That should be enough for the example you described.
It is also possible to use MockHttpServletRequest.html to set-up the http request in the way you want.
MockHttpServletRequest servletRequest = new MockHttpServletRequest();
servletRequest.setServerName("www.example.com");
servletRequest.setRequestURI("/v1/someuri");
servletRequest.addParameter("brand1", "value1");
servletRequest.addParameter("brand2", "value2");
servletRequest.addParameter("another-param", "another-value");
ServletWebRequest servletWebRequest = new ServletWebRequest(servletRequest);
assertThat("brand names ", servletWebRequest.getParameterMap(), hasEntry("brand1", "value1"));
It is particularly useful in cases such as ServletWebRequest whose 'getRequestURI()' method, for instance, is a final method and hence cannot be mocked. So, instead of mocking we can simply pass in a web request that we built, like the one above.

java - get cookie value by name in spring mvc

I'm working on a java spring mvc application. I have set a cookie in one of my controller's methods in this way:
#RequestMapping(value = {"/news"}, method = RequestMethod.GET)
public ModelAndView news(Locale locale, Model model, HttpServletResponse response, HttpServletRequest request) throws Exception {
...
response.setHeader("Set-Cookie", "test=value; Path=/");
...
modelAndView.setViewName("path/to/my/view");
return modelAndView;
}
This is working fine and I can see a cookie with name test and value "value" in my browser console. Now I want to get the cookie value by name in other method. How can I get value of test cookie?
The simplest way is using it in a controller with the #CookieValue annotation:
#RequestMapping("/hello")
public String hello(#CookieValue("foo") String fooCookie) {
// ...
}
Otherwise, you can get it from the servlet request using Spring org.springframework.web.util.WebUtils
WebUtils.getCookie(HttpServletRequest request, String cookieName)
By the way, the code pasted into the question could be refined a bit. Instead of using #setHeader(), this is much more elegant:
response.addCookie(new Cookie("test", "value"));
You can also use org.springframework.web.util.WebUtils.getCookie(HttpServletRequest, String).
private String getCookieValue(HttpServletRequest req, String cookieName) {
return Arrays.stream(req.getCookies())
.filter(c -> c.getName().equals(cookieName))
.findFirst()
.map(Cookie::getValue)
.orElse(null);
}
Spring MVC already gives you the HttpServletRequest object, it has a getCookies() method that returns Cookie[] so you can iterate on that.
private String extractCookie(HttpServletRequest req) {
for (Cookie c : req.getCookies()) {
if (c.getName().equals("myCookie"))
return c.getValue();
}
return null;
}
Cookie doesnt have method to get by value try this
Cookie cookie[]=request.getCookies();
Cookie cook;
String uname="",pass="";
if (cookie != null) {
for (int i = 0; i < cookie.length; i++) {
cook = cookie[i];
if(cook.getName().equalsIgnoreCase("loginPayrollUserName"))
uname=cook.getValue();
if(cook.getName().equalsIgnoreCase("loginPayrollPassword"))
pass=cook.getValue();
}
}

Catch GET request parameters in a Map<String,String>

I'm trying to catch key value pairs in a Map parameter at spring MVC side. This looks to me to be something simple but I can't wrap my head around it at the moment. Take following url
www.goudengids.be.localhost:8080/ms/view/sendContactForm.ajah?pageId=711408&listingId=685592&siteId=353009&url=http%3A%2F%2Fwww.goudengids.be.localhost%3A8080%2Fms%2Fms%2Fkbc-bank-versicherung-recht-4780%2Fms-353009-preview%2F&moduleId=65920100&mySiteId=353009&pageShortId=1&prefills[Naam]=janneke
You'll notice at the end my latest attempt to get this working prefills[Naam]=janneke. I like to catch this in the following controller.
public String getContactForm(#RequestParam(required = true) Long moduleId, #RequestParam(required = true) String url, #RequestParam(required=false) Map<String,String> prefills, Long mySiteId, Integer pageShortId,
DefaultPageParameters defaultPageParameters, ModelMap model, HttpServletRequest request, HttpServletResponse response, Locale locale) throws Exception {
However I'm recieving all parameters in the request in my prefills variable instead of just Naam,janneke. Is this even possible what I'm attempting or should I go with a large string with a token to tokenize ?
prefills=naam:janneke|title:maan|subject:space
I couldn't find a clean way out, so I went for the pragmatic solution
public String getContactForm(#RequestParam(required = true) Long moduleId, #RequestParam(required = true) String url, #RequestParam(required=false) List<String> prefills, Long mySiteId, Integer pageShortId,
DefaultPageParameters defaultPageParameters, ModelMap model, HttpServletRequest request, HttpServletResponse response, Locale locale) throws Exception {
private void prefillFieldsWithData(List<String> prefills, ModelMap model, BasicContactFormVo contactFormVo) {
if(prefills != null && !prefills.isEmpty()){
Map<String, String> valuesOfCustomFields = new HashMap<String, String>();
List<ContactFormElementVo> customFormElements = contactFormVo.getCustomFormElements();
for (String prefillData : prefills) {
if(prefillData.contains("|")){
String[] prefillFieldData = prefillData.split("|");
for (ContactFormElementVo contactFormElementVo : customFormElements) {
if(contactFormElementVo.getLabel().equals(prefillFieldData[0])){
valuesOfCustomFields.put("cfe"+contactFormElementVo.getId().toString(), prefillFieldData[1]);
break;
}
}
}
}
model.addAttribute("customFieldValues",valuesOfCustomFields);
}
}
It's a bit sad I have to do it this way, but it seems Spring has a generic way of detecting a Map as request parameter and filling it with all parameters in the request. There is no way around that except overloading that class which I rather do not considering it's part of the entire MVC mechanic.
I call the controller now with following URL. It works just ... meh ... not my favorite solution
http://www.goudengids.be.localhost:8080/ms/view/sendContactForm.ajah?pageId=711408&listingId=685592&siteId=353009&url=http%3A%2F%2Fwww.goudengids.be.localhost%3A8080%2Fms%2Fms%2Fkbc-bank-versicherung-recht-4780%2Fms-353009-preview%2F&moduleId=65920100&mySiteId=353009&pageShortId=1&prefills=Naam|janneke%20zag%20eens%20pruimen&prefills=E-mail|maan

"Bad Request" when attempting to send array to spring mvc controller

I saw related questions and tried those none of those helped. I'm sending POST requst with jquery like this :
var data = {};
//this works every time and it's not issue
var statusArray = $("#status").val().split(',');
var testvalue = $("#test").val();
data.test = testvalue;
data.status = statusArray ;
$.post("<c:url value="${webappRoot}/save" />", data, function() {
})
On the controller side I've tried following :
public void saveStatus(ModelMap model, Principal principal, HttpSession session, final HttpServletResponse response, #RequestParam String test, #RequestBody String [] status) {
//I never get to this point, but when I set statusArray to required false test variable is being populated correctly
}
public void saveStatus(ModelMap model, Principal principal, HttpSession session, final HttpServletResponse response, #RequestParam String test, #RequestParam String [] status) {
//I never get to this point, but when I set statusArray to required false test variable is being populated correctly
}
public void saveStatus(ModelMap model, Principal principal, HttpSession session, final HttpServletResponse response, #RequestParam String test, #RequestParam("status") String [] status) {
//I never get to this point, but when I set statusArray to required false test variable is being populated correctly
}
public void saveStatus(ModelMap model, Principal principal, HttpSession session, final HttpServletResponse response, #RequestParam String test, #RequestParam(name="status") String [] status) {
//I never get to this point, but when I set statusArray to required false test variable is being populated correctly
}
none of these worked I'm wondering what I'm doing wrong, whatever I do I get Bad request
Your status param should be #RequestParam(value = "status[]") String[] status (Spring 3.1).
I have also gone through the same problem of Bad request. I resolved it by doing the following code.
You can post an array to a controller by converting it into json string by JSON.stringify(array).
I have pushed muliple Objects into an array using push().
var a = [];
for(var i = 1; i<10; i++){
var obj = new Object();
obj.name = $("#firstName_"+i).val();
obj.surname = $("#lastName_"+i).val();
a.push(obj);
}
var myarray = JSON.stringify(a);
$.post("/ems-web/saveCust/savecustomers",{myarray : myarray},function(e) {
}, "json");
Controller :
You can use jackson for processing json string.
Jackson is a High-performance JSON processor Java library.
#RequestMapping(value = "/savecustomers", method = RequestMethod.POST)
public ServiceResponse<String> saveCustomers(ModelMap model, #RequestParam String myarray) {
try{
ObjectMapper objectMapper = new ObjectMapper().configure(DeserializationFeature.ACCEPT_SINGLE_VALUE_AS_ARRAY, true);
List<DtoToMAP> parsedCustomerList = objectMapper.readValue(myarray, new TypeReference<List<DtoToMAP>>() { });
System.out.println(" parsedCustomerList :: " + parsedCustomerList);
}catch (Exception e) {
System.out.println(e);
}
}
Note : make sure that your dto should contain same variable name as you are posting with an array object.
in my case, my dto contains firstName,lastName as am posting with an array object.
Jackson Dependancy :
<dependency>
<groupId>org.codehaus.jackson</groupId>
<artifactId>jackson-core-asl</artifactId>
<version>1.9.3</version>
</dependency>
<dependency>
<groupId>org.codehaus.jackson</groupId>
<artifactId>jackson-mapper-asl</artifactId>
<version>1.9.3</version>
</dependency>
I think your problem might be that to send an array to something you have to actually send the param multiple times.
In the case of a GET operation something like: ?status=FOO&status=BAR
I'm not sure spring would convert a comma separated string to an array for you automagically. You could however, add a PropertyEditor (see PropertyEditorSupport) to split the string on commas.
#InitBinder
public void initBinder(WebDataBinder binder) {
binder.registerCustomEditor(String[].class, new PropertyEditorSupport() {
#Override
public String getAsText() {
String value[] = (String[]) getValue();
if (value == null) {
return "";
}
else {
return StringUtils.join(value, ",");
}
}
#Override
public void setAsText(String text) throws IllegalArgumentException {
if (text == null || text.trim().length() == 0) {
setValue(null);
}
else {
setValue(StrTokenizer.getCSVInstance(text).getTokenArray());
}
}
});
}
Note, i'm using commons-lang to both join and split the string, but you could easily just do it yourself using any means you want.
Once you do this, any time you want a parameter bound to a String[] from a single string, spring will automatically convert it for you.

Categories

Resources