Java Optional.ofNullable with Mockito and ObjectMapper - java

I have function like following to generate an object of provided type from any payload:
public static <T> Optional<T> generateObject(String payloadJson, Class<T> type) {
T objectPayload = null;
try {
objectPayload = objectMapper.readValue(payloadJson, type);
}catch (IOException e) {
log.info(e.getMessage());
}
return Optional.ofNullable(objectPayload);
}
Had written the test case as follows
#Test
void generateObject() throws Exception {
Mockito.when( eventUtil.generateObject("a", Mockito.any(Class.class)) ).thenReturn( Optional.of("") );
Mockito.<Optional<Object>>when( eventUtil.generateObject("b", Mockito.any(Class.class)) ).thenReturn( Optional.of("") );
Optional<Object> o2 = eventUtil.generateObject("b", Mockito.any(Class.class));
assertEquals( "lol", o2.get() );
Mockito.verify(eventUtil);
}
The test execution is failing on Mockito.when statement with
java.lang.IllegalArgumentException: Unrecognized Type: [null]
**Note: I have tried providing null, another model class and even the parent class itself with thenReturn( Optional.of()) with no luck. Please point me to the correct direction in this case.

As explained Why doesn't Mockito mock static methods?
you cannot mock static method by using mockito.
Anyway I guess your goal is to check if this utility method behave as you expect in case the objectMapper throws exception or just works fine. So you can make the method non static, then you can inject the objectMapper and try something like:
#Test
void objectMapperThrowsException() throws Exception {
ObjectMapper mapper = Mockito.mock(ObjectMapper.class);
EventUtil eventUtil = new EventUtil(mapper);
Mockito.when(mapper.readValue(any(),any())).thenThrow(IOException.class);
Optional<Object> result = eventUtil.generateObject("b", Object.class);
assertTrue(result.isEmpty());
}
and the positive case
#Test
void objectMapperReturnAValidObject() throws Exception {
Object anObject = new Object();
ObjectMapper mapper = Mockito.mock(ObjectMapper.class);
EventUtil eventUtil = new EventUtil(mapper);
Mockito.when(mapper.readValue(any(),any())).thenReturn(anObject);
Optional<Object> result = eventUtil.generateObject("b", Object.class);
assertFalse(result.isEmpty());
assertEquals(anObject, result.get());
}

The best method to test this method is to convert any data type to JSON and call the method to convert to the original one, try to use a helper class or any other data type
public class TargetTest {
#Test
void generateObject() throws Exception {
// Arrange
ObjectMapper mapper = new ObjectMapper();
Helper helper = new Helper();
helper.setName("test");
String json = mapper.writeValueAsString(helper );
// Act
Helper actual = EventUtil.generateObject(json, Helper.class);
// Assert
}
class Helper {
private String name;
// Getter and setter
}
}
, If you want to mock it, try to use PowerMockito

Related

Unit testing static method- GsonSerializer

I need to junit test a piece of code, but the GsonConverter it calls from different class is in static method that i cannot change. I haven't a clue how to proceed as i cant mock it due to it being static.
public String fetchEntity(Object retValue, Object[] args) {
String refDet= null;
List<Details> updatedDetails = null;
if (retValue != null && retValue instanceof List && ((List) retValue).stream()
.noneMatch((o -> !(o instanceof Details)))) {
updatedDetails = (List<Details>) retValue;
} else {
logger.warn("Error");
return null;
}
try {
refDet= GsonConverter.serialize(updatedDetails );
} catch (Exception e) {
logger.warn("Error updatedDetails ");
}
return refDet;
}
Here is the class with static methods
class GsonConverter{
public static final Gson GSON = new GsonBuilder()
.registerTypeAdapter(DateTime.class, (JsonDeserializer<DateTime>) (dateTime, type, context) -> ISODateTimeFormat.dateTime().parseDateTime(dateTime.getAsString()))
.create();
public static String serialize(Object o) {
return GSON.toJson(o);
}
}
The simplest thing would be to hide the direct use of GsonConverter behind an object instance. Perhaps something like:
interface JsonMapper {
String toJsonString(Object o);
}
class GsonJsonMapper implements JsonMapper {
String toJsonString(Object o) {
return GsonConverter.serialize(o);
}
}
Now in your original code, depend on the interface (JsonMapper) but instantiate it as an GsonJsonMapper (ideally using a dependency injection framework like Guice or Spring).
// declare an instance of type JsonMapper
private JsonMapper mapper;
public String fetchEntity(Object retValue, Object[] args) {
// skip the first part ...
try {
// use the mapper
refDet = mapper.serialize(updatedDetails );
} catch (Exception e) {
logger.warn("Error updatedDetails ");
}
return refDet;
}
Now you have the ability to mock out the JsonMapper interface.
You will often encounter this type of situation -- code that was not written to be testable often must change in order to add tests. Which is why many developers practice TDD, or at least write unit tests immediately after writing the new code.

jackson Include.NOT_NULL on field not working

Using JsonInclude(content=Include.NON_NULL) (or actually any other JsonInclude options) seems to have no effect on serialization of my objects. Consider this test:
public class JsonTest {
#Data
#JsonInclude(content=Include.ALWAYS)
static class TestObj {
String fieldVisible = "a";
String fieldVisibleNull = null;
#JsonInclude(content=Include.NON_NULL)
String fieldInvisible = null;
}
public static void main(String[] args) throws JsonProcessingException {
ObjectMapper mapper = new ObjectMapper();
String json = mapper.writerFor(TestObj.class).writeValueAsString(new TestObj());
if (json.contains("fieldInvisible")) {
System.err.println(json);
throw new RuntimeException("null field written even though it shouldn't be");
}
}
}
The result is:
{"fieldVisible":"a","fieldVisibleNull":null,"fieldInvisible":null}
Exception in thread "main" java.lang.RuntimeException: null field written even though it shouldn't be
at my.package.JsonTest.main(JsonTest.java:57)
What am I missing? What don't I understand?
Using
#JsonInclude(Include.NON_NULL)
Instead of
#JsonInclude(content=Include.NON_NULL)
Works. Thanks #Aaron.

CustomDeserializer has no default (no arg) constructor

I am consuming a REST Api with RestTemplate. The response I'm getting from the API has lots of nested objects. Here's a little snippet as an example:
"formularios": [
{
"form_data_id": "123006",
"form_data": {
"form_data_id": "123006",
"form_id": "111",
"efs": {
"1": {},
"2": "{\"t\":\"c\",\"st\":\"m\",\"v\":[{\"id\":\"3675\",\"l\":\"a) Just an example\",\"v\":\"1\"},{\"id\":\"3676\",\"l\":\"b) Another example.\",\"v\":\"2\"}]}"
}
}
The problem I'm having is that most of the times the "1" actually has content, just like "2", and the jackson just parses it as a String on the object "efs". But sometimes, just like in the code snippet, the API sends it empty, and jackson takes it as an Object, which gives me an error that says something about START_OBJECT (can't remember the exact error, but it's not important for this question).
So I decided to make a custom deserializer so when jackson reads "1", it ignores the empty object and just parses it as a null string.
Here's my custom deserializer:
public class CustomDeserializer extends StdDeserializer<Efs> {
public CustomDeserializer(Class<Efs> t) {
super(t);
}
#Override
public Efs deserialize(JsonParser jp, DeserializationContext dc)
throws IOException, JsonProcessingException {
String string1 = null;
String string2 = null;
JsonToken currentToken = null;
while ((currentToken = jp.nextValue()) != null) {
if (currentToken.equals(JsonToken.VALUE_STRING)) {
if (jp.getCurrentName().equals("1")) {
string1 = jp.getValueAsString();
} else {
string2 = jp.getValueAsString();
}
} else {
if (jp.getCurrentName().equals("2")) {
string2 = jp.getValueAsString();
}
}
}
return new Efs(string1, string2);
}
}
And this is the way I'm using it when receiving the response from the API:
ObjectMapper mapper = new ObjectMapper();
SimpleModule mod = new SimpleModule("EfsModule");
mod.addDeserializer(Efs.class, new CustomDeserializer(Efs.class));
mapper.registerModule(mod);
List<HttpMessageConverter<?>> messageConverters = new ArrayList<>();
MappingJackson2HttpMessageConverter jsonMessageConverter = new MappingJackson2HttpMessageConverter();
jsonMessageConverter.setObjectMapper(mapper);
messageConverters.add(jsonMessageConverter);
RestTemplate restTemplate = new RestTemplate();
restTemplate.setMessageConverters(messageConverters);
I'm getting the error:
CustomDeserializer has no default (no arg) constructor
But I don't know exactly what I'm doing wrong nor how to solve it. Thanks for the help and apologies for the long question, I wanted to give as much context as possible.
There is also one trap that users can fall into (like my self). If you declare deserializer as a inner class (not a static nested class) like:
#JsonDeserialize(using = DomainObjectDeserializer.class)
public class DomainObject {
private String key;
public class DomainObjectDeserializer extends StdDeserializer<DomainObject> {
public DomainObjectDeserializer() {
super(DomainObject.class);
}
#Override
public DomainObject deserialize(JsonParser p, DeserializationContext ctxt) throws IOException {
// code
}
}
}
Jackson uses the Class#getDeclaredConstructor() with no argument (method accepts vararg) which means: give me a default (no argument) constructor. Code above will throw exception when Jackson tries to create DomainObjectDeserializer because javac generates the constructor that accepts enclosing class reference. Technically speaking DomainObjectDeserializer does not have a default constructor.
For a curiosity sake you can execute DomainObjectDeserializer.class.getDeclaredConstructors() and ensure that method does return single element array containing constructor definition with enclosing class reference.
The DomainObjectDeserializer should be declared as a static class.
Here is a good answer to read in more details.
It is required that you have a default constructor without arguments.
What you can do is create one (or replace the other one if you don't really need it):
public class CustomDeserializer extends StdDeserializer<Efs> {
public CustomDeserializer() {
super(Efs.class);
}
...
}

How to wrap a List as top level element in JSON generated by Jackson

I am running into a problem where I am trying to include a List as the root node, but I can't seem to be able to get this. Let me explain. Let's say we have a class "TestClass"
class TestClass{
String propertyA;
}
Now, in some utility method this is what I do
String utilityMethod(){
List<TestClass> list = someService.getList();
new ObjectMapper().writeValueAsString(list);
}
The output I am trying to get in JSON is
{"ListOfTestClasses":[{"propertyA":"propertyAValue"},{"propertyA":"someOtherPropertyValue"}]}
I have tried to use
objMapper.getSerializationConfig().set(Feature.WRAP_ROOT_VALUE, true);
But, I still don't seem to get it right.
Right now, I am just creating a Map < String,TestClass > and I write that to achieve what I am trying to do, which works but clearly this is a hack. Could someone please help me with a more elegant solution? Thanks
Unfortunately, even with the WRAP_ROOT_VALUE feature enabled you still need extra logic to control the root name generated when serializing a Java collection (see this answer for details why). Which leaves you with the options of:
using a holder class to define the root name
using a map.
using a custom ObjectWriter
Here is some code illustrating the three different options:
public class TestClass {
private String propertyA;
// constructor/getters/setters
}
public class TestClassListHolder {
#JsonProperty("ListOfTestClasses")
private List<TestClass> data;
// constructor/getters/setters
}
public class TestHarness {
protected List<TestClass> getTestList() {
return Arrays.asList(new TestClass("propertyAValue"), new TestClass(
"someOtherPropertyValue"));
}
#Test
public void testSerializeTestClassListDirectly() throws Exception {
final ObjectMapper mapper = new ObjectMapper();
mapper.configure(SerializationFeature.WRAP_ROOT_VALUE, true);
System.out.println(mapper.writeValueAsString(getTestList()));
}
#Test
public void testSerializeTestClassListViaMap() throws Exception {
final ObjectMapper mapper = new ObjectMapper();
final Map<String, List<TestClass>> dataMap = new HashMap<String, List<TestClass>>(
4);
dataMap.put("ListOfTestClasses", getTestList());
System.out.println(mapper.writeValueAsString(dataMap));
}
#Test
public void testSerializeTestClassListViaHolder() throws Exception {
final ObjectMapper mapper = new ObjectMapper();
final TestClassListHolder holder = new TestClassListHolder();
holder.setData(getTestList());
System.out.println(mapper.writeValueAsString(holder));
}
#Test
public void testSerializeTestClassListViaWriter() throws Exception {
final ObjectMapper mapper = new ObjectMapper();
final ObjectWriter writer = mapper.writer().withRootName(
"ListOfTestClasses");
System.out.println(writer.writeValueAsString(getTestList()));
}
}
Output:
{"ArrayList":[{"propertyA":"propertyAValue"},{"propertyA":"someOtherPropertyValue"}]}
{"ListOfTestClasses":[{"propertyA":"propertyAValue"},{"propertyA":"someOtherPropertyValue"}]}
{"ListOfTestClasses":[{"propertyA":"propertyAValue"},{"propertyA":"someOtherPropertyValue"}]}
{"ListOfTestClasses":[{"propertyA":"propertyAValue"},{"propertyA":"someOtherPropertyValue"}]}
Using an ObjectWriter is very convenient - just bare in mind that all top level objects serialized with it will have the same root name. If thats not desirable then use a map or holder class instead.
I'd expect the basic idea to be something like:
class UtilityClass {
List listOfTestClasses;
UtilityClass(List tests) {
this.listOfTestClasses = tests;
}
}
String utilityMethod(){
List<TestClass> list = someService.getList();
UtilityClass wrapper = new UtilityClass(list);
new ObjectMapper().writeValueAsString(wrapper);
}

mockito stubbing returns null

I am using mockito as mocking framework. I have a scenerio here, my when(abc.method()).thenReturn(value) does not return value, instead it returns null.
Here is how my class and test looks like.
public class foo(){
public boolean method(String userName) throws Exception {
ClassA request = new ClassA();
request.setAbc(userName);
ClassB response = new ClassB();
try {
response = stub.callingmethod(request);
} catch (Exception e) {
}
boolean returnVal = response.isXXX();
return returnVal;
}
Now follwoing is the test
#Test
public void testmethod() throws Exception{
//arrange
String userName = "UserName";
ClassA request = new ClassA();
ClassB response = new ClassB();
response.setXXX(true);
when(stub.callingmethod(request)).thenReturn(response);
//act
boolean result = fooinstance.lockLogin(userName);
//assert
assertTrue(result);
}
stub is mocked using mockito i.e using #Mock. The test throws NullPointerException in class foo near boolean retrunVal = response.isXXX();
the argument matcher for stub.callingmethod(request).thenReturn(response) is comparing for reference equality. You want a more loose matcher, like this I think:
stub.callingmethod(isA(ClassA.class)).thenReturn(response);
Ensure that your ClassA implements its own equals and that it is correctly implemented.

Categories

Resources