Compare 2 objects ignoring null values - java

The project uses TestNg, Java11, Spring test
I am writing testNG tests for API
I have a java object that has this kind of stucture:
class Object1
private Object2 o2;
private List<Object3> o3;
Where Object2 is not only composed of primitive attributes.
I would like to test if 2 Object1 are equals with these rules:
if actual o2 is null, don't fail even if the other o2 is not
if actual o3 is null or empty, don't fail even if the other o3 is not
if actual o3 is not null nor empty, compare only non null Object3 fields
To sum up, I would like to assert that 2 objects are the same, ignoring null fields recursively.
I could do it
assertThat(actual).usingRecursiveComparison().ignoringActualNullFields().isEqualTo(other);
but the recursive null fields are not ignored.
How can I fix this?

You can also ignore expected null fields..
Can you also provide a simple test to reproduce the issue?
Feel free to raise an issue in https://github.com/joel-costigliola/assertj-core/issues

For me following code worked:
public class SocialLink {
private String platform;
private String link;
}
SocialLink obj1 = new SocialLink("Facebook", null);
SocialLink obj2 = new SocialLink("Facebook", null);
assertThat(obj1).isEqualToIgnoringNullFields(obj2);

I finally created my own asserts like this:
import org.assertj.core.api.AbstractAssert;
import org.assertj.core.api.Assertions;
import java.util.List;
import java.util.stream.Collectors;
public class Object1Assert extends AbstractAssert<Object1Assert, Object1> {
public Object1Assert isEqualTo(Object1 other) {
// specially for null
if(actual == other) {return this;}
if(actual.getObject2() != null) {
Assertions.assertThat(other.getObject2()).isEqualToIgnoringNullFields(actual.getObject2());
}
if(actual.getObject3() != null) {
for(Object3 object3 : actual.getObject3()) {
my.package.Assertions.assertThat(object3).isIn(other.getObject3());
}
}
// return the current assertion for method chaining
return this;
}
public Object1Assert(Object1 actual) {
super(actual, Object1Assert.class);
}
public static Object1Assert assertThat(Object1 actual) {
return new Object1Assert(actual);
}
}
public class Assertions {
public static Object3Assert assertThat(Object3 actual) {
return new Object3Assert(actual);
}
}
public class Object3Assert extends AbstractAssert<Object3Assert, Object3> {
public Object3Assert isIn(List<Object3> others) {
List<String> otherStringIds = others.stream().map(Object3::getStringId).collect(Collectors.toList());
Assertions.assertThat(otherStringIds).isNotEmpty();
Assertions.assertThat(actual.getStringId()).isIn(otherStringIds);
for (Object3 otherObject3 : others) {
if(actual.getStringId().equalsIgnoreCase(otherObject3.getStringId())) {
Assertions.assertThat(otherObject3).usingComparatorForType(Comparators.bigDecimalComparator, BigDecimal.class).isEqualToIgnoringNullFields(actual);
}
}
// return the current assertion for method chaining
return this;
}
public Object3Assert(Object3 actual) {
super(actual, Object3Assert.class);
}
public static Object3Assert assertThat(Object3 actual) {
return new Object3Assert(actual);
}
}
I created this class for each type I needed with this tutorial
https://www.baeldung.com/assertj-custom-assertion

Related

System.identityHashCode is equal on String instances

because of reasons I am trying to copy/clone instances of objects. And in the case of String i tried something like this:
I do have an object like
class Foo{
private String test;
//Getters && Setters are generated
}
and a copy method like:
private static Object copyMemberData(Object originalMemberData) {
if (originalMemberData == null) {
return null;
}
...
if (originalMemberData instanceof String) {
return String.valueOf(originalMemberData);
}
...
}
which is used like
PropertyDescriptor propDesc = new PropertyDescriptor("test", Foo.class);
//Get Data from original object
final Object originalMemberData = propDesc.getReadMethod().invoke(originalFoo);
final Object copiedMemberData = copyMemberData(originalMemberData);
And afterwards I tried to compare the result with System.identityHashCode to ensure that I am not working on a reference.
if (System.identityHashCode(copiedMemberData) == System.identityHashCode(originalMemberData)) {
throw new RuntimeException("Cloning is buggy!");
}
And I am suprised this actually matches and throws me an error. Maybe someone can explain me the reason for that.
I found it out :-)
The String is the same even if I do compare it with == instead of equals. This is the case because the toString() method of the String.java class which is used in String.valueOf(Object obj) is implemented like:
public String toString() {
return this;
}
To successfully copy a String use:
return new String(((String)originalMemberData).toCharArray());

use generics to replace specific class type

Currently, I have three classes, Apple, Grape, Banana, and I created a converter class (can convert a string to list) for each of them. Then I realized all those converter classes are basically the same except they take different class types.
The example of AppleConverter.java
public class AppleConverter extends StrutsTypeConverter {
#SuppressWarnings("rawtypes")
#Override
public Object convertFromString(Map context, String[] values, Class toClass) {
final List<Apple> appleList = new ArrayList<>();
try {
for (String appleString : values) {
Apple apple = Apple.valueOf(appleString);
appleList.add(apple);
}
}
catch (IllegalArgumentException e) {
throw new TypeConversionException(e);
}
return appleList;
}
#SuppressWarnings("rawtypes")
#Override
public String convertToString(Map context, Object o) {
if (o == null) {
return null;
}
if (o instanceof List<?>) {
List<Apple> list = (List<Apple>) o;
String appleString = list
.stream()
.map(e -> String.valueOf(e))
.collect(Collectors.joining(","));
return appleString;
}
throw new TypeConversionException("wrong");
}
Basically, GrapdeConverter.java and BananaConverter.java are the same as AppleConverter.java except they take different class types.
So I tried to create a generic class then those converter classes can inherit it.
My generic class code:
public class FruitConverter<T> extends StrutsTypeConverter {
#SuppressWarnings("rawtypes")
#Override
public Object convertFromString(Map context, String[] values, Class toClass) {
if (ArrayUtils.isEmpty(values)) {
return null;
}
final List<T> objs = new ArrayList<>();
try {
for (String objStr : values) {
Object obj = toClass.getDeclaredMethod("valueOf", String.class).invoke(null, objStr);
objs.add(obj);
}
}
}
catch (IllegalArgumentException e) {
throw new TypeConversionException(e);
}
return objs;
}
#SuppressWarnings("rawtypes")
#Override
public String convertToString(Map context, Object o) {
if (o == null) {
return null;
}
if (o instanceof List<?>) {
List<?> list = (List<?>) o;
String fruitString = list
.stream()
.map(e -> String.valueOf(e))
.collect(Collectors.joining(","));
return fruitString;
}
throw new TypeConversionException("object is not a list of objs");
}
}
When I called T obj = T.valueOf(objStr);, it throws an error can not resolve method valueOf.
May anyone tell me how to apply generics correctly in this case.
The compilation error occurs because Java's generics are implemented with type erasure. In this case, T.valueOf is treated as the same as if you had tried to do Object.valueOf, i.e. it doesn't know which classes to search in for the valueOf method except for Object.
You can pass the method in as a function using a method reference, as in the following example:
import java.util.function.Function;
class FruitConverter<T> {
private final Function<String, T> valueOf;
FruitConverter(Function<String, T> valueOf) {
this.valueOf = valueOf;
}
// ...
}
class AppleConverter extends FruitConverter<Apple> {
AppleConverter() {
super(Apple::valueOf);
}
}
Then you can use this.valueOf.apply(...) when you want to call the method.
Also see https://docs.oracle.com/javase/tutorial/java/javaOO/methodreferences.html.
You cannot use T in such expressions, I doubt that you even need generics for this task.
Quick and dirty way to generalize is to use reflection, instead of T obj = T.valueOf(objStr); use this:
Object obj = toClass.getDeclaredMethod("valueOf", String.class).invoke(null, objStr);
then you will be able to use same function for all objects like this.
Another approach is to use fabrics, but it will require much more code.

method returning either a collection or a single value

I have a class with various properties and I would like to write a wrapper method around them in order to loop around them more easily.
Some properties return a collection of values, some a single value. And I'm looking for the best approach for this.
My first approach is to let the wrapper method return whatever the property getters return.
public class Test {
public Object getValue(String propName) {
if ("attr1".equals(propName)) return getAttribute1();
else if ("attr2".equals(propName)) return getAttribute2();
else return null;
}
public List<String> getAttribute1() {
return Arrays.asList("Hello","World");
}
public String getAttribute2() {
return "Goodbye";
}
public static void main(String[] args) {
final Test test=new Test();
Stream.of("attr1","attr2")
.forEach(p-> {
Object o=test.getValue(p);
if (o instanceof Collection) {
((Collection) o).forEach(v->System.out.println(v));
}
else {
System.out.println(o);
}
});
}
}
The bad point with this approach is that the caller has to test himself whether the result is a collection or not.
Other approach, seamless for the caller, is to always return a collection, ie. the wrapper function wraps the single values into a Collection. Here an HashSet, but we can imagine an adhoc, minimum 1 element list.
public class TestAlt {
public Collection getValue(String propName) {
if ("attr1".equals(propName))
return getAttribute1();
else if ("attr2".equals(propName)) {
Set s = new HashSet();
s.add(getAttribute2());
return s;
}
else
return null;
}
public List<String> getAttribute1() {
return Arrays.asList("Hello", "World");
}
public String getAttribute2() {
return "Goodbye";
}
public static void main(String[] args) {
final TestAlt test = new TestAlt();
Stream.of("attr1", "attr2")
.forEach(p -> {
test.getValue(p).forEach(v -> System.out.println(v));
});
}
Performance-wise, design-wise, ... what's your opinion on these approaches ? Do you have better ideas ?
Well, you could pass the action to be performed on each attribute to the object and let the object decide on how to handle it. E.g.:
in Class Test:
public void forEachAttribute(String propName, Handler h) {
if ("attr1".equals(propName))
h.handle(getAttribute1());
else if ("attr2".equals(propName)) {
getAttribute2().forEach(o -> h.handle(o))
}
}
And a class Handler with the function handle(String s), that does, what you want to do.
If you cannot edit Test, you can also move the function outside Test
public void forEachTestAttribute(Test t, String propName, Handler h)...
Performance-wise: This removes an if-clause
Design-wise: This removes a cast, but creates more classes.
*Edit: It also maintains type-security, and if there are multiple kinds of attributes (String, int, etc.) you could add more handle-functions, to still maintain type-security.
Regarding the design I would rewrite your code into this:
TestAlt.java
import java.util.*;
import java.util.stream.Stream;
public class TestAlt {
private Map<String, AttributeProcessor> map = AttributeMapFactory.createMap();
public Collection getValue(String propName) {
return Optional
.ofNullable(map.get(propName))
.map(AttributeProcessor::getAttribute)
.orElse(Arrays.asList("default")); //to avoid unexpected NPE's
}
public static void main(String[] args) {
final TestAlt test = new TestAlt();
Stream.of("attr1", "attr2")
.forEach(p -> test.getValue(p).forEach(v -> System.out.println(v)));
}
}
AttributeMapFactory.java
import java.util.HashMap;
import java.util.Map;
public class AttributeMapFactory {
public static Map<String, AttributeProcessor> createMap() {
Map<String, AttributeProcessor> map = new HashMap<>();
map.put("attr1", new HiAttributeProcessor());
map.put("attr2", new ByeAttributeProcessor());
return map;
}
}
AttributeProcessor.java
import java.util.Collection;
public interface AttributeProcessor {
Collection<String> getAttribute();
}
HiAttributeProcessor.java
import java.util.Arrays;
import java.util.Collection;
public class HiAttributeProcessor implements AttributeProcessor{
#Override
public Collection<String> getAttribute() {
return Arrays.asList("Hello", "World");
}
}
ByeAttributeProcessor.java
import java.util.Arrays;
import java.util.Collection;
public class ByeAttributeProcessor implements AttributeProcessor{
#Override
public Collection<String> getAttribute() {
return Arrays.asList("Goodbye");
}
}
The main point is that you get rid of if-else statements using map and dynamic dispatch.
The main advantage of this approach is that your code becomes more flexible to further changes. In case of this small programm it does not really matter and is an overkill. But if we are talking about large enterprise application, then yes, it becomes crucial.

Orika: Map from String to a List of SomeObjects

Consider the following situation:
public class A {
private String stringA;
public String getStringA() {
return stringA;
}
public void setStringA(String stringA) {
this.stringA = stringA;
}
}
public class B {
List<SomeObject> someObjects;
public List<SomeObject> getSomeObjects() {
if (someObjects == null) {
someObjects = new ArrayList<SomeObject>();
}
return someObjects;
}
}
public class SomeObject {
private String stringSomeObject;
public String getStringSomeObject() {
return stringSomeObject;
}
public void setStringSomeObject(String stringSomeObject) {
this.stringSomeObject = stringSomeObject;
}
}
I want to map from A to B. Whilst mapping these, stringA needs to be mapped to stringSomeObject in SomeObject. I tried to write a Orika-Mapper for this:
public class MyMapper extends ConfigurableMapper {
#Override
protected void configure(MapperFactory factory) {
ConverterFactory converterFactory = factory.getConverterFactory();
converterFactory.registerConverter(new StringToSomeObjectConverter());
factory.classMap(A.class, B.class) //
.field("stringA", "someObjects") //
.byDefault() //
.register();
}
}
It maps class A to B and whenever it encounters a conversion from String to List<SomeObject> it calls a custom-converter:
public class StringToSomeObjectConverter extends CustomConverter<String, List<SomeObject>> {
private static final String BORROWER_PARTY_TYP_CODE = "147";
#Override
public List<SomeObject> convert(String source, Type<? extends List<SomeObject>> destinationType) {
SomeObject someObject = new SomeObject();
someObject.setStringSomeObject(source);
return Arrays.asList(someObject);
}
}
I wrote an unit-test to ensure that this works:
#Test
public void testMap() throws Exception {
A a = new A();
a.setStringA("a");
B outcome = new MyMapper().map(a, B.class);
assertThat(outcome.getSomeObjects.size(), is(1));
}
Sadly this test fails with:
java.lang.AssertionError:
Expected: is <1>
but: was <0>
It seems like the Converter is never executed so I tried to debug it. And indeed: The debugger never reaches the converter. Am I doing something wrong? It seems like. I know there are more methods which one could go with like: mapAToB e.g...
Ok I found a solut...nah! It's not a solution, it's just a workaround. I defined the stringA as List<String> as well and defined a converter extending CustomConverter<String, LoanContrReqERPCrteReqLoanContrBrrwrPty>.
Because this feels a little "hacky", I am still interested in a nice solution. (Though I am just thinking that this solution might be fine: Now the datastructure of both objects is more equal than before. The problem is, that object B is coming from an external service, I can't modify it.)
You mapping doesn't work because you don't have setter for someObjects.
When Orika tries to generate code for mapper, it checks all fieldMaps in classMap for sourceProperty is readable and destinationProperty is assignable. If this checks passed, generator puts field conversion into generated mapper. If check failed, Orika just skip this field conversion.
Few options you can use to solve a problem:
You can add setter for someObjects field in class B:
public static class B {
List<SomeObject> someObjects;
public List<SomeObject> getSomeObjects() {
if (someObjects == null) {
someObjects = new ArrayList<SomeObject>();
}
return someObjects;
}
public void setSomeObjects(List<SomeObject> someObjects) {
this.someObjects = someObjects;
}
}
Use custom mapper instead of converter:
factory.classMap(A.class, B.class)
.customize(
new CustomMapper<A, B>() {
#Override
public void mapAtoB(A a, B b, MappingContext context) {
SomeObject someObject = new SomeObject();
someObject.setStringSomeObject(a.getStringA());
b.getSomeObjects().add(someObject);
}
}
)
.byDefault()
.register();
Orika will put invocation customMapper after resolving of field maps.
Generated mapper will looks like:
b.setOtherField(a.getOtherField());
if (customMapper != null) {
customMapper.map(source, destination); <-- Your mapper invocation
}
Use follow syntax for fields:
factory.classMap(A.class, B.class)
.field("stringA", "someObjects[0].stringSomeObject")
.byDefault()
.register();
Generated mapper will looks like:
if (source.getStringA() != null) {
if (((((java.util.List) destination.getSomeObjects()).size() <= 0 || ((List) destination.getSomeObjects()).get(0) == null))) {
((java.util.List) destination.getSomeObjects()).add(0, ((BoundMapperFacade) usedMapperFacades[0]).newObject(((String) source.getStringA()), mappingContext));
}
}
if (!(((java.lang.String) source.getStringA()) == null)) {
(((java.util.List) destination.getSomeObjects()).get(0)).setStringSomeObject(source.getStringA());
} else if (!(((java.util.List) destination.getSomeObjects()) == null) && !((((java.util.List) destination.getSomeObjects()).size() <= 0 || ((List) destination.getSomeObjects()).get(0) == null))) {
( ((java.util.List) destination.getSomeObjects()).get(0)).setStringSomeObject(null);
}
Also there was a bug in Orika to map from single property to property of collection using syntax .field("stringA", "elements{stringB}") ( Incorrect mapper code generated for mapping from a single property to property of collection element). Bug closed at 31 Dec 2016 here: Fix for bug

Test if a class is serializable

What I need to do is having a unit test that checks whether a given class MyClass is serializable. But I can't only check that the class implements Serializable: all the attributes of the class (and the attributes of the attributes etc.) have to implement Serializable.
Also, a solution like creating an instance of MyClass and try to serialize and deserialize it is not satisfactory: if someone adds an attribute and does not update the unit test, the unit test won't test the new attribute.
One solution would probably be to use reflection recursively to test whether the classes of all the attributes implement Serializable, but that seems really heavy.
Isn't there a better solution?
Thanks.
This is an old question, but I thought I would add my 2c.
Even if you recurse through the whole object, you cannot guarantee that your object implements serializable. If you have a member variable that is abstract or is an interface, you cannot say if the object you eventually store will or will not be serialized. If you know what you are doing, it would serialized.
This answer provides a solution by populating your objects with random data, but this is probably overkill.
I have created a class that does recurse through the structure ignoring interfaces, java.lang.Object and abstract classes.
package au.com.tt.util.test;
import java.io.Serializable;
import java.lang.reflect.Field;
import java.lang.reflect.Modifier;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.HashSet;
import java.util.List;
import java.util.Set;
public final class SerializableChecker
{
public static class SerializationFailure
{
private final String mContainingClass;
private final String mMemberName;
public SerializationFailure(String inNonSerializableClass, String inMemberName)
{
mContainingClass = inNonSerializableClass;
mMemberName = inMemberName;
}
public String getContainingClass()
{
return mContainingClass;
}
public String getMemberName()
{
return mMemberName;
}
public String getBadMemberString()
{
if (mMemberName == null)
return mContainingClass;
return mContainingClass + "." + mMemberName;
}
#Override
public String toString()
{
return "SerializationFailure [mNonSerializableClass=" + mContainingClass + ", mMemberName=" + mMemberName + "]";
}
}
private static class SerializationCheckerData
{
private Set<Class<?>> mSerializableClasses;
SerializationCheckerData()
{
mSerializableClasses = new HashSet<Class<?>>();
}
boolean isAlreadyChecked(Class<?> inClass)
{
return mSerializableClasses.contains(inClass);
}
void addSerializableClass(Class<?> inClass)
{
mSerializableClasses.add(inClass);
}
}
private SerializableChecker()
{ }
public static SerializationFailure isFullySerializable(Class<?> inClass)
{
if (!isSerializable(inClass))
return new SerializationFailure(inClass.getName(), null);
return isFullySerializable(inClass, new SerializationCheckerData());
}
private static SerializationFailure isFullySerializable(Class<?> inClass, SerializationCheckerData inSerializationCheckerData)
{
for (Field field : declaredFields(inClass))
{
Class<?> fieldDeclaringClass = field.getType();
if (field.getType() == Object.class)
continue;
if (Modifier.isStatic(field.getModifiers()))
continue;
if (field.isSynthetic())
continue;
if (fieldDeclaringClass.isInterface() || fieldDeclaringClass.isPrimitive())
continue;
if (Modifier.isAbstract(field.getType().getModifiers()))
continue;
if (inSerializationCheckerData.isAlreadyChecked(fieldDeclaringClass))
continue;
if (isSerializable(fieldDeclaringClass))
{
inSerializationCheckerData.addSerializableClass(inClass);
SerializationFailure failure = isFullySerializable(field.getType(), inSerializationCheckerData);
if (failure != null)
return failure;
else
continue;
}
if (Modifier.isTransient(field.getModifiers()))
continue;
return new SerializationFailure(field.getDeclaringClass().getName(), field.getName());
}
return null;
}
private static boolean isSerializable(Class<?> inClass)
{
Set<Class<?>> interfaces = getInterfaces(inClass);
if (interfaces == null)
return false;
boolean isSerializable = interfaces.contains(Serializable.class);
if (isSerializable)
return true;
for (Class<?> classInterface : interfaces)
{
if (isSerializable(classInterface))
return true;
}
if (inClass.getSuperclass() != null && isSerializable(inClass.getSuperclass()))
return true;
return false;
}
private static Set<Class<?>> getInterfaces(Class<?> inFieldDeclaringClass)
{
return new HashSet<Class<?>>(Arrays.asList(inFieldDeclaringClass.getInterfaces()));
}
private static List<Field> declaredFields(Class<?> inClass)
{
List<Field> fields = new ArrayList<Field>(Arrays.asList(inClass.getDeclaredFields()));
Class<?> parentClasses = inClass.getSuperclass();
if (parentClasses == null)
return fields;
fields.addAll(declaredFields(parentClasses));
return fields;
}
}
As a general check, you can use SerializationUtils from Apache Commons, as follows:
byte [] data = SerializationUtils.serialize(obj);
Object objNew = SerializationUtils.deserialize(data);
but it does not guarantee that other instances of that class will serialize properly; for complex objects there is a risk that particular members will not be serializable depending on their members.
Using reflection recursively is not perfect but could partially makes the job.
To do that, you can re-use an utility class like this.
Usage will look like this :
public class SerializationTests {
#Test
public void ensure_MyClass_is_serializable() {
assertIsSerializable(MyClass.class);
}
#Test
public void ensure_MyComplexClass_is_serializable() {
// We excludes "org.MyComplexClass.attributeName"
// because we can not be sure it is serializable in a
// reliable way.
assertIsSerializable(MyComplexClass.class, "org.MyComplexClass.attributeName");
}
private static void assertIsSerializable(Class<?> clazz, String... excludes) {
Map<Object, String> results = SerializationUtil.isSerializable(clazz, excludes);
if (!results.isEmpty()) {
StringBuilder issues = new StringBuilder();
for (String issue : results.values()) {
issues.append("\n");
issues.append(issue);
}
fail(issues.toString());
}
}
}
I think serialize and deserialize instance is OK. If class is changed unit test will still be valid, you need to compare original and deserialized objects
An easy way to test if an object is serializable in Kotlin :
/**
* Check if an object is Serializable
*/
fun assertIsSerializable(obj: Any) {
val serialized = SerializationUtils.serialize(obj as Serializable)
val unSerialized = SerializationUtils.deserialize<Any>(serialized)
}

Categories