I'm trying to find a way to fix some unit test at work. We use javers.compare()
to log the differences after we modified an object, but a strange error came up and i've found little to none info about it.
All of our objects implements the Serializable interface, and when passed to javers it processes them correctly, in the unit test when i pass a mocked object in, javers runs normally, unless the object has another object in it. I've made the inner objects implement the Serializable interface, i've tried to make them transient, nothing worked. It always fails.
As i said, object are being processed normally if they are instanced, but the mocked version of the same objects are causing the error.
An object from this class when mocked by mockito doesn't cause an error when compared with javers.compare()
.
public class SomeEntry implements Serializable {
private Long id;
private String aString;
private Double aNumber;
private List<Long> aListOfNumbers;
}
An object from this class only cause an error with javers.compare()
when it's mocked by mockito, it doesn't if instanced normally.
public class OtherEntry implements Serializable {
private Long id;
private String aString;
private Double aNumber;
private SomeOtherEntry someOtherEntry;
}
public class SomeOtherEntry implements Serializable {
private Long id;
}
Making SomeOtherEntry
inside of OtherEntry
transient does nothing and the error comes up.
This is the error i javers.compare() produces with the mocked object
java.lang.UnsupportedOperationException: Attempted to serialize java.lang.Class: {classpath}/{classname} Forgot to register a type adapter?
at com.google.gson.internal.bind.TypeAdapters$1.write(TypeAdapters.java:73)
at com.google.gson.internal.bind.TypeAdapters$1.write(TypeAdapters.java:69)
at com.google.gson.TypeAdapter$1.write(TypeAdapter.java:191)
at com.google.gson.internal.bind.TypeAdapterRuntimeTypeWrapper.write(TypeAdapterRuntimeTypeWrapper.java:69)
at com.google.gson.internal.bind.ReflectiveTypeAdapterFactory$1.write(ReflectiveTypeAdapterFactory.java:127)
at com.google.gson.internal.bind.ReflectiveTypeAdapterFactory$Adapter.write(ReflectiveTypeAdapterFactory.java:245)
at com.google.gson.internal.bind.TypeAdapterRuntimeTypeWrapper.write(TypeAdapterRuntimeTypeWrapper.java:69)
at com.google.gson.internal.bind.ReflectiveTypeAdapterFactory$1.write(ReflectiveTypeAdapterFactory.java:127)
at com.google.gson.internal.bind.ReflectiveTypeAdapterFactory$Adapter.write(ReflectiveTypeAdapterFactory.java:245)
at com.google.gson.internal.bind.TypeAdapterRuntimeTypeWrapper.write(TypeAdapterRuntimeTypeWrapper.java:69)
at com.google.gson.internal.bind.ReflectiveTypeAdapterFactory$1.write(ReflectiveTypeAdapterFactory.java:127)
at com.google.gson.internal.bind.ReflectiveTypeAdapterFactory$Adapter.write(ReflectiveTypeAdapterFactory.java:245)
at com.google.gson.Gson.toJson(Gson.java:704)
at com.google.gson.Gson.toJsonTree(Gson.java:597)
at com.google.gson.Gson.toJsonTree(Gson.java:576)
at com.google.gson.internal.bind.TreeTypeAdapter$GsonContextImpl.serialize(TreeTypeAdapter.java:155)
at org.javers.core.json.typeadapter.commit.CdoSnapshotStateTypeAdapter.lambda$toJson$0(CdoSnapshotStateTypeAdapter.java:28)
at org.javers.core.metamodel.object.CdoSnapshotState.lambda$forEachProperty$1(CdoSnapshotState.java:61)
at java.util.HashMap$EntrySet.forEach(HashMap.java:1044)
at org.javers.core.metamodel.object.CdoSnapshotState.forEachProperty(CdoSnapshotState.java:61)
at org.javers.core.json.typeadapter.commit.CdoSnapshotStateTypeAdapter.toJson(CdoSnapshotStateTypeAdapter.java:28)
at org.javers.core.json.typeadapter.commit.CdoSnapshotStateTypeAdapter.toJson(CdoSnapshotStateTypeAdapter.java:13)
at org.javers.core.json.JsonConverterBuilder.lambda$registerJsonTypeAdapterForType$3(JsonConverterBuilder.java:167)
at com.google.gson.internal.bind.TreeTypeAdapter.write(TreeTypeAdapter.java:81)
at com.google.gson.internal.bind.TreeTypeAdapter.write(TreeTypeAdapter.java:74)
at com.google.gson.Gson.toJson(Gson.java:704)
at com.google.gson.Gson.toJson(Gson.java:683)
at com.google.gson.Gson.toJson(Gson.java:638)
at com.google.gson.Gson.toJson(Gson.java:618)
at org.javers.core.json.JsonConverter.toJson(JsonConverter.java:54)
at org.javers.core.snapshot.ObjectHasher.hash(ObjectHasher.java:28)
at org.javers.core.metamodel.object.GlobalIdFactory.createId(GlobalIdFactory.java:69)
at org.javers.core.graph.LiveCdoFactory.create(LiveCdoFactory.java:29)
at org.javers.core.graph.LiveCdoFactory.create(LiveCdoFactory.java:15)
at org.javers.core.graph.EdgeBuilder$MultiEdgeContainerBuilderFunction.apply(EdgeBuilder.java:72)
at org.javers.core.metamodel.type.CollectionType.mapToList(CollectionType.java:47)
at org.javers.core.metamodel.type.CollectionType.map(CollectionType.java:34)
at org.javers.core.graph.EdgeBuilder.createMultiEdge(EdgeBuilder.java:55)
at org.javers.core.graph.ObjectGraphBuilder.buildMultiEdges(ObjectGraphBuilder.java:91)
at org.javers.core.graph.ObjectGraphBuilder.buildEdges(ObjectGraphBuilder.java:71)
at org.javers.core.graph.ObjectGraphBuilder.buildGraphFromCdo(ObjectGraphBuilder.java:58)
at org.javers.core.graph.ObjectGraphBuilder.buildGraph(ObjectGraphBuilder.java:47)
at org.javers.core.graph.LiveGraphFactory.createLiveGraph(LiveGraphFactory.java:39)
at org.javers.core.diff.DiffFactory.buildGraph(DiffFactory.java:99)
at org.javers.core.diff.DiffFactory.compare(DiffFactory.java:54)
at org.javers.core.JaversCore.compare(JaversCore.java:173)
at The class that is being tested.
at The Unit Test.
at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62)
at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
at java.lang.reflect.Method.invoke(Method.java:498)
at org.junit.runners.model.FrameworkMethod$1.runReflectiveCall(FrameworkMethod.java:50)
at org.junit.internal.runners.model.ReflectiveCallable.run(ReflectiveCallable.java:12)
at org.junit.runners.model.FrameworkMethod.invokeExplosively(FrameworkMethod.java:47)
at org.junit.internal.runners.statements.InvokeMethod.evaluate(InvokeMethod.java:17)
at org.junit.internal.runners.statements.RunBefores.evaluate(RunBefores.java:26)
at org.junit.runners.ParentRunner.runLeaf(ParentRunner.java:325)
at org.junit.runners.BlockJUnit4ClassRunner.runChild(BlockJUnit4ClassRunner.java:78)
at org.junit.runners.BlockJUnit4ClassRunner.runChild(BlockJUnit4ClassRunner.java:57)
at org.junit.runners.ParentRunner$3.run(ParentRunner.java:290)
at org.junit.runners.ParentRunner$1.schedule(ParentRunner.java:71)
at org.junit.runners.ParentRunner.runChildren(ParentRunner.java:288)
at org.junit.runners.ParentRunner.access$000(ParentRunner.java:58)
at org.junit.runners.ParentRunner$2.evaluate(ParentRunner.java:268)
at org.junit.runners.ParentRunner.run(ParentRunner.java:363)
at org.junit.runner.JUnitCore.run(JUnitCore.java:137)
at com.intellij.junit4.JUnit4IdeaTestRunner.startRunnerWithArgs(JUnit4IdeaTestRunner.java:68)
at com.intellij.rt.junit.IdeaTestRunner$Repeater.startRunnerWithArgs(IdeaTestRunner.java:33)
at com.intellij.rt.junit.JUnitStarter.prepareStreamsAndStart(JUnitStarter.java:230)
at com.intellij.rt.junit.JUnitStarter.main(JUnitStarter.java:58)
Javers is designed to work with static classes, so classes that you write. You should avoid passing to Javers dynamic proxies generated on-the-fly by Hibernate or Mockito. Those classes add some technical fields which you definetly don't wont to be compered and versioned. See https://javers.org/documentation/domain-configuration/#ignoring-things
My advice is, avoid mocking your own classes, especially entities. It would make your test stronger and less complicated.