How to Mock HttpHeaders inside MultivaluedMap - java

In my test class, I have framed it as
public class TestDummy {
private AClass a;
private final HttpHeaders mockHeader = Mockito.mock(HttpHeaders.class);
private final MultivaluedMap<String, String> mockMultiHeaderMap =
Mockito.mock(MultivaluedMap.class);
#BeforeEach
void beforeMethod(){
Mockito.when(mockMultiHeaderMap.getFirst("abc")).thenReturn("def);
}
void actualTestClass(){
Assertions.assertDoesNotThrow(() -> {
return a.method(mockHeader);
});
}
}
This is my actual dummy class
public AClass dummy{
public void method(HttpHeaders headers){
MultivaluedMap<String, String> multiHeaderMap = headers.getRequestHeaders();
String val = multiHeaderMap.getFirst("abc");
}
}
I know that I am setting value to mockMultiHeaderMap but I am not getting that how to update my testcase since, in actual AClass we have to pass HttpHeaders object itself on method().

Related

Java: How to Mock a protected method inside a static child class

I am having a protected method inside a static child class. I am running a testcase , its getting successful but code coverage is not increasing.
public class A{
private static final String var1 = "key1";
protected static class B extends OsCmd{
private String abc1;
private String abc2;
protected B(String xyz, String xyz2) {
this.abc1 = xyz;
this.abc2 = xyz2;
}
#Override
protected void updateEnv(Map<String, String> env) {
env.put(VAR1, "FALSE");
env.put(VAR2, "TRUE");
env.put("key3", abc1);
env.put("key4", abc2);
}
}
}
Below is my test case
#ExtendWith(MockitoExtension.class)
public class ATest {
private A mockA;
#BeforeEach
public void setup() {
mockA = Mockito.spy(A.class);
}
#Test
public void test2() {
try (MockedConstruction mockedConstruction =
mockConstruction(A.B.class)) {
Map<String, String> map = new HashMap<String, String>();
map.put("key1", "value1");
A.B mockB =
new A.B("a", "b");
//doNothing().when(mockB).updateEnv(map);
mockB.updateEnv(map);
}
}
}
Can someone please help here, what mistake i am doing?
When you mock the constructor, then all internal method calls are also mocked and do not go through the actual code.
If you remove the following try-with-resources:
try (MockedConstruction mockedConstruction =
mockConstruction(A.B.class))
The real code will be executed and the coverage will increase.

Dynamodb attribute converter provider for enhanced type extending Hashmap

I have a type which is extending HashMap<String, String>. As per the documentation here, it is possible to add a custom converter for the type. But it seems not working. The contents of the hashMap doesn't get converted, output looks like below;
"summary": {
"en": null
},
Any idea how to convert Label and its fields along with it's hashmap's contents?
Parent
#DynamoDbBean(converterProviders = {
CustomAttributeConverterProvider.class,
DefaultAttributeConverterProvider.class})
public class Summary extends BaseEntry {
private #Valid Label summary = null;
}
Child
#DynamoDbBean(converterProviders = {
CustomAttributeConverterProvider.class,
DefaultAttributeConverterProvider.class})
public class Label extends HashMap<String, String> {
private #Valid String en = null;
}
HashMapAttributeConverter
public class HashMapAttributeConverter implements AttributeConverter<Map<String, String>> {
private static AttributeConverter<Map<String, String>> mapConverter;
/** Default constructor. */
public HashMapAttributeConverter() {
mapConverter =
MapAttributeConverter.builder(EnhancedType.mapOf(String.class, String.class))
.mapConstructor(HashMap::new)
.keyConverter(StringStringConverter.create())
.valueConverter(StringAttributeConverter.create())
.build();
}
#Override
public AttributeValue transformFrom(Map<String, String> input) {
return mapConverter.transformFrom(input);
}
#Override
public Map<String, String> transformTo(AttributeValue input) {
return mapConverter.transformTo(input);
}
#Override
public EnhancedType<Map<String, String>> type() {
return mapConverter.type();
}
#Override
public AttributeValueType attributeValueType() {
return mapConverter.attributeValueType();
}
}
CustomAttributeConverterProvider
public class CustomAttributeConverterProvider implements AttributeConverterProvider {
private final List<AttributeConverter<?>> customConverters =
Arrays.asList(new HashMapAttributeConverter());
private final Map<EnhancedType<?>, AttributeConverter<?>> customConvertersMap;
private final AttributeConverterProvider defaultProvider =
DefaultAttributeConverterProvider.create();
public CustomAttributeConverterProvider() {
customConvertersMap =
customConverters.stream().collect(Collectors.toMap(AttributeConverter::type, c -> c));
}
#Override
public <T> AttributeConverter<T> converterFor(EnhancedType<T> enhancedType) {
return (AttributeConverter<T>)
customConvertersMap.computeIfAbsent(enhancedType, defaultProvider::converterFor);
}
}
#Override
public <T> AttributeConverter<T> converterFor(EnhancedType<T> type) {
// in this method you have to return only your converter based on type
// otherwise null should be returned. It will fix your issue.
}

How to pass hashmap as payload in RES and how to test it through POSTMAN

I have a rest API with the following URL
#PostMapping(path = "/Employees/employees")
private ResponseEntity<Map<String, BigDecimal>> availabilityCalculator(#RequestBody ReqOb req, Map<String, BigDecimal> testMap) {}
what annotation should I use for the map(like RequestBody for Object). Can I use RequestBody itself considering the map is also a type of object? 2. How should I pass - a hashmap and an object as payload for testing it through POSTMAN
Create a class that will contain that map and pass it like that. For example:
public class CalculationStatsDto {
private Map<String, BigDecimal> testMap;
public CalculationStatsDto () {
}
public Map<String, BigDecimal> getTestMap() {
return testMap;
}
public void setTestMap(Map<String, BigDecimal> testMap) {
this.testMap = testMap;
}
}
And your rest method should be:
#PostMapping(path = "/Employees/employees")
private ResponseEntity<CalculationStatsDto> availabilityCalculator(#RequestBody
CalculationStatsDto calculationStatsDto) {}
And if you need also that 'ReqOb req' in the request body then you can put it in your entity class:
public class CalculationStatsDto {
private Map<String, BigDecimal> testMap;
private ReqOb req;
public CalculationStatsDto() {
}
public Map<String, BigDecimal> getTestMap() {
return testMap;
}
public void setTestMap(Map<String, BigDecimal> testMap) {
this.testMap = testMap;
}
public ReqOb getReq() {
return req;
}
public void setReq(ReqOb req) {
this.req = req;
}
}
With the last one you will wrap both of it in one request body.

Mapping URL parameters with dashes to object in Spring Web MVC

Mapping URL request parameters with Spring MVC to an object is fairly straightforward if you're using camelCase parameters in your request, but when presented with hyphen delimited values, how do you map these to an object?
Example for reference:
Controller:
#RestController
public class MyController {
#RequestMapping(value = "/search", method = RequestMethod.GET)
public ResponseEntity<String> search(RequestParams requestParams) {
return new ResponseEntity<>("my-val-1: " + requestParams.getMyVal1() + " my-val-2: " + requestParams.getMyVal2(), HttpStatus.OK);
}
}
Object to hold parameters:
public class RequestParams {
private String myVal1;
private String myVal2;
public RequestParams() {}
public String getMyVal1() {
return myVal1;
}
public void setMyVal1(String myVal1) {
this.myVal1 = myVal1;
}
public String getMyVal2() {
return myVal2;
}
public void setMyVal2(String myVal2) {
this.myVal2 = myVal2;
}
}
A request made like this works fine:
GET http://localhost:8080/search?myVal1=foo&myVal2=bar
But, what I want is for a request with hyphens to map to the object, like so:
GET http://localhost:8080/search?my-val-1=foo&my-val-2=bar
What do I need to configure in Spring to map url request parameters with hyphens to fields in an object? Bear in mind that we may have many parameters, so using a #RequestParam annotation for each field is not ideal.
I extended ServletRequestDataBinder and ServletModelAttributeMethodProcessor to solve the problem.
Consider that your domain object may already be annotated with #JsonProperty or #XmlElement for serialization. This example assumes this is the case. But you could also create your own custom annotation for this purpose e.g. #MyParamMapping.
An example of your annotated domain class is:
public class RequestParams {
#XmlElement(name = "my-val-1" )
#JsonProperty(value = "my-val-1")
private String myVal1;
#XmlElement(name = "my-val-2")
#JsonProperty(value = "my-val-2")
private String myVal2;
public RequestParams() {
}
public String getMyVal1() {
return myVal1;
}
public void setMyVal1(String myVal1) {
this.myVal1 = myVal1;
}
public String getMyVal2() {
return myVal2;
}
public void setMyVal2(String myVal2) {
this.myVal2 = myVal2;
}
}
You will need a SerletModelAttributeMethodProcessor to analyze the target class, generate a mapping, invoke your ServletRequestDataBinder.
public class KebabCaseProcessor extends ServletModelAttributeMethodProcessor {
public KebabCaseProcessor(boolean annotationNotRequired) {
super(annotationNotRequired);
}
#Autowired
private RequestMappingHandlerAdapter requestMappingHandlerAdapter;
private final Map<Class<?>, Map<String, String>> replaceMap = new ConcurrentHashMap<Class<?>, Map<String, String>>();
#Override
protected void bindRequestParameters(WebDataBinder binder, NativeWebRequest nativeWebRequest) {
Object target = binder.getTarget();
Class<?> targetClass = target.getClass();
if (!replaceMap.containsKey(targetClass)) {
Map<String, String> mapping = analyzeClass(targetClass);
replaceMap.put(targetClass, mapping);
}
Map<String, String> mapping = replaceMap.get(targetClass);
ServletRequestDataBinder kebabCaseDataBinder = new KebabCaseRequestDataBinder(target, binder.getObjectName(), mapping);
requestMappingHandlerAdapter.getWebBindingInitializer().initBinder(kebabCaseDataBinder, nativeWebRequest);
super.bindRequestParameters(kebabCaseDataBinder, nativeWebRequest);
}
private static Map<String, String> analyzeClass(Class<?> targetClass) {
Field[] fields = targetClass.getDeclaredFields();
Map<String, String> renameMap = new HashMap<String, String>();
for (Field field : fields) {
XmlElement xmlElementAnnotation = field.getAnnotation(XmlElement.class);
JsonProperty jsonPropertyAnnotation = field.getAnnotation(JsonProperty.class);
if (xmlElementAnnotation != null && !xmlElementAnnotation.name().isEmpty()) {
renameMap.put(xmlElementAnnotation.name(), field.getName());
} else if (jsonPropertyAnnotation != null && !jsonPropertyAnnotation.value().isEmpty()) {
renameMap.put(jsonPropertyAnnotation.value(), field.getName());
}
}
if (renameMap.isEmpty())
return Collections.emptyMap();
return renameMap;
}
}
This KebabCaseProcessor will use reflection to get a list of mappings for your request object. It will then invoke the KebabCaseDataBinder - passing in the mappings.
#Configuration
public class KebabCaseRequestDataBinder extends ExtendedServletRequestDataBinder {
private final Map<String, String> renameMapping;
public KebabCaseRequestDataBinder(Object target, String objectName, Map<String, String> mapping) {
super(target, objectName);
this.renameMapping = mapping;
}
protected void addBindValues(MutablePropertyValues mpvs, ServletRequest request) {
super.addBindValues(mpvs, request);
for (Map.Entry<String, String> entry : renameMapping.entrySet()) {
String from = entry.getKey();
String to = entry.getValue();
if (mpvs.contains(from)) {
mpvs.add(to, mpvs.getPropertyValue(from).getValue());
}
}
}
}
All that remains now is to add this behavior to your configuration. The following configuration overrides the default configuration that the #EnableWebMVC delivers and adds this behavior to your request processing.
#Configuration
public static class WebContextConfiguration extends WebMvcConfigurationSupport {
#Override
protected void addArgumentResolvers(List<HandlerMethodArgumentResolver> argumentResolvers) {
argumentResolvers.add(kebabCaseProcessor());
}
#Bean
protected KebabCaseProcessor kebabCaseProcessor() {
return new KebabCaseProcessor(true);
}
}
Credit should be given to #Jkee. This solution is derivative of an example he posted here: How to customize parameter names when binding spring mvc command objects.
One way I can think of getting around the hyphens is to use HttpServletRequestWrapper class to wrap the original request.
Parse all the request parameters in this class and convert all hyphenated parameters into camelcase. After this, spring will be able to automatically map those parameters to your POJO classes.
public class CustomRequestWrapper extends HttpServletRequestWrapper {
private Map<String, String> camelCasedParams = new Hashmap();
public CustomRequestWrapper(HttpServletRequest req){
//Get all params from request.
//Transform each param name from hyphenated to camel case
//Put them in camelCasedParams;
}
public String getParameter(String name){
return camelCasedParams.get(name);
}
//Similarly, override other methods related to request parameters
}
Inject this request wrapper from J2EE filter. You can refer to below link for a tutorial on injecting request wrappers using filter.
http://www.programcreek.com/java-api-examples/javax.servlet.http.HttpServletRequestWrapper
Update your web xml to include filter and its filter mapping.

Mock private static method in final class using PowerMockito

I have a final class with private static method which is invoked inside another static method
public final class GenerateResponse{
private static Map<String, String> getErrorDetails(JSONObject jsonObject) {
// implementation
}
public static String method1(params...){
Map<String, String> map = getErrorDetails(new JsonObject());
// implementation
}
}
I need to mock the private static method call getErrorDetails(), but my test is calling the actual method. Here is my code:
#RunWith(PowerMockRunner.class)
#PrepareForTest(GenerateResponse.class)
public class GenerateResponseTest{
#Test
public void testFrameQtcErrorResponse() throws Exception {
Map<String, String> errorDtls = new HashMap<String, String>();
PowerMockito.spy(GenerateResponse.class);
PowerMockito.doReturn(errorDtls).when(GenerateResponse.class, "getErrorDetails", JSONObject.class);
String response = GenerateResponse.method1(params...);
}
You should use an argument matcher in the whenmethod. I've modified your code a little bit to run the test case.
Actual method
public final class GenerateResponse{
private static Map<String, String> getErrorDetails(JSONObject jsonObject) {
return null;
}
public static String method1() {
Map<String, String> map = getErrorDetails(new JSONObject());
return map.get("abc");
}
}
Test method
#RunWith(PowerMockRunner.class)
#PrepareForTest(GenerateResponse.class)
public class GenerateResponseTest {
#Test
public void testFrameQtcErrorResponse() throws Exception {
Map<String, String> errorDtls = new HashMap<String, String>();
errorDtls.put("abc", "alphabets");
PowerMockito.mockStatic(GenerateResponse.class, Mockito.CALLS_REAL_METHODS);
PowerMockito.doReturn(errorDtls).when(GenerateResponse.class,
"getErrorDetails", Matchers.any(JSONObject.class));
String response = GenerateResponse.method1();
System.out.println("response =" + response);
}
}
Output
response =alphabets

Categories