0

Consider you are doing some integration testing, you are storing some bigger entity into db, and then read it back and would like to compare it. Obviously it has some associations as well, but that's just a cherry on top of very unpleasant cake. How do you compare those entities? I saw lot of incorrect ideas and feel, that this has to be written manually. How you guys do that?

Issues:

  • you cannot use equals/hashcode: these are for natural Id.
  • you cannot use subclass with fixed equals, as that would test different class and can give wrong results when persisting data as data are handled differently in persistence context.
  • lot of fields: you don't want to type all comparisons by hand. You want reflection.
  • @Temporal annotations: you cannot use trivial "reflection equals" approaches, because @Temporal(TIMESTAMP) java.util.Date <> java.sql.Date
  • associations: typical entity you would like to have properly tested will have several associations, thus tool/approach ideally should support deep comparison. Also cycles in object graph can ruin the fun.

Best solution what I found:

  • don't use transmogrifying data types (like Date) in JPA entities.
  • all associations should be initialized in entity, because null <> empty list.
  • calculate externaly toString via say ReflectionToStringBuilder, and compare those. Reason for that is to allow entity to have its toString, tests should not depend that someone does not change something. Theoretically, toString can be deep, but commons recursive toStringStyle includes object identifier, which ruins it.
  • I though, that I could use json format to string, but commons support that only for shallow toString, Jackson (without further instructions on entity) fails on cycles over associations

Alternative solution would be actually declaring subclasses with generated id (say lombok) and use some automatic mapping tool (say remondis mapper), with option to overcome differences in Dates/collections.

But I'm listening. Does anyone posses better solution?

Martin Mucha
  • 2,385
  • 1
  • 29
  • 49
  • I don't understand the problem with Date. Aren't you comparing an entity VS entity? the fields should be the same no? What about trying a diff library such as https://java-object-diff.readthedocs.io/en/latest/ ? – paranoidAndroid Dec 14 '18 at 22:16
  • one way to implement date column is: @Temporal(Timestamp) Date date; When you're persisting the entity, you can pass java.util.Date to that field, but when you read it from db, you will get java.sql.Timestamp subclass. – Martin Mucha Dec 15 '18 at 17:38
  • Sure, but when you are reading it from the DB using ORM (jpa) you will get it with the java type (java.util.Date) and not java.sql.Time. Hibernate or eclipse link is taking care of these conversions automatically. So I don't understand exactly where is the problem. Can you post a sample of a test? – paranoidAndroid Dec 16 '18 at 19:49
  • 1
    well... I'm not sure it's true what you're saying. At least on my machine it seems, that in entity there is java.util.Date defined, but reference is of subclass java.sql.Timestamp. I discovered it by comparing before and after entities, having nowhere java.sql.Timestamp used, and yet it still got into comparison. IIUC, hibernate isn't taking care of that. And it makes sense, repackaging to java.util.Date would cost some time and GC time. – Martin Mucha Dec 17 '18 at 11:12
  • Well, it's interesting what you say. When I come to think about it, I've never tried it myself so I'm probably wrong. Maybe this can help you: https://stackoverflow.com/questions/9533935/how-to-force-hibernate-to-return-dates-as-java-util-date-instead-of-timestamp – paranoidAndroid Dec 17 '18 at 20:48

0 Answers0