Wiki

Clone wiki

deep-difference-utils / Home

About

The purpose of the deep-difference-utils library is to provide a convenient way of comparing objects in tests, including a deep recursive comparison of inner beans.

For example, let's assume you have a web application and different layers of entities: one layer is for DB, another layer is for a controller. With the help of this library, you can check in your tests that, for example, entity (from DB layer) is "deep equal" to corresponding DTO (from controller level), validating the correctness of the translation, between layers.

The configuration includes numerous ways of excluding properties from comparison, so only what you need will be compared.

Usage

Add it as maven dependency:

#!xml

<dependency>
  <groupId>org.bitbucket.brunneng.deepdiff</groupId>
  <artifactId>deep-difference-utils</artifactId>
  <version>1.0.0</version>
</dependency>

Let's assume we have the next model to work with:

#!java
   class Person {
      String name;
      int age;
      Address address;

      public Person(String name, int age, Address address) {
         this.name = name;
         this.age = age;
         this.address = address;
      }
   }

   class Address {
      String town;
      String street;

      public Address(String town, String street) {
         this.town = town;
         this.street = street;
      }
   }

   class PersonDTO {
      String name;
      int age;
      AddressDTO address;

      public PersonDTO(String name, int age, AddressDTO address) {
         this.name = name;
         this.age = age;
         this.address = address;
      }
   }

   class AddressDTO {
      String town;
      String street;

      public AddressDTO(String town, String street) {
         this.town = town;
         this.street = street;
      }
   }

The main class of this library is DeepDiffFinder, using it you can actually find differences:

#!java
   Person p = new Person("Harry", 25, null);
   PersonDTO dto = new PersonDTO("Harry", 24, null);
   System.out.println(new DeepDiffFinder().findDifferences(p, dto));
prints:
[Not equal values in path <"age">: <25> != <24>]

Method findDifferences returns the object of type Differences extends ArrayList<Difference> which provides methods to filter, search and exclude found differences.

Also, you may notice that the library works with fields, but if getters will be defined for them - then getters will be used to access the values.

Let's try another example:

#!java
   Person p = new Person("Harry", 25, new Address("London", "Great street"));
   PersonDTO dto = new PersonDTO("Harry", 25, new AddressDTO("Norwich", "Great street"));
   System.out.println(new DeepDiffFinder().findDifferences(p, dto));
prints:
[Not equal values in path <"address.town">: <London> != <Norwich>]
As you can see, library goes "deep" to search all differences. Now let's try to add some field, which will exist only in person, but not it corresponding DTO. Our field will be String secret;
#!java
   Person p = new Person("Harry", 25, null, "Little secret");
   PersonDTO dto = new PersonDTO("Harry", 25, null);
   System.out.println(new DeepDiffFinder().findDifferences(p, dto));
prints:
[Path <"secret"> exists only in 1st object]
So it's detected another type of difference, that path secret exists only in the first object, but not in DTO.

If you want that such kind of differences shouldn't be detected and that it should compare only common fields, then it could be achieved like this:

#!java
   Person p = new Person("Harry", 25, null, "Little secret");
   PersonDTO dto = new PersonDTO("Harry", 25, null);

   DeepDiffFinder finder = new DeepDiffFinder(new Configuration().compareOnlyCommonProperties());
   System.out.println(finder.findDifferences(p, dto));
prints:
[]
No difference, because field "secret" isn't compared now.

By default, all beans are compared recursively. If you want to compare bean by equals it can be explicitly configured:

#!java
   Person p = new Person("Harry", 25, new Address("London", "Great street"), null);
   PersonDTO dto = new PersonDTO("Harry", 25, new AddressDTO("London", "Great street"));

   Configuration config = new Configuration();
   config.compareOnlyCommonProperties();
   config.beanOfClass(Address.class).applyCompareByEquals();

   DeepDiffFinder finder = new DeepDiffFinder(config);
   System.out.println(finder.findDifferences(p, dto));
prints:
   [Not equal values in path <"address">: <org.bitbucket.brunneng.deepdiff.UsageExample$Address@1bce4f0a> != <org.bitbucket.brunneng.deepdiff.UsageExample$AddressDTO@5e3a8624>]
Despite addressed are same by structure, they are compared by equals, and equals returns 'false', so such difference is detected. Using method beanOfClass, we are accessing here sub configuration related to bean Address.

The same effect could be achieved by configuring compare by equals on the property level:

#!java
   Person p = new Person("Harry", 25, new Address("London", "Great street"), null);
   PersonDTO dto = new PersonDTO("Harry", 25, new AddressDTO("London", "Great street"));

   Configuration config = new Configuration();
   config.compareOnlyCommonProperties();
   config.beanOfClass(Person.class).property("address").applyCompareByEquals();

   DeepDiffFinder finder = new DeepDiffFinder(config);
   System.out.println(finder.findDifferences(p, dto));
It prints the same output, as the previous example. Using method property, we are accessing here sub configuration related to property "address" of bean Person.

Library detects the next types of differences:

EqualsDifference - Difference between two values which were compared by 'equals' and it returned 'false'.

OnlyOnePathExistsDifference - Difference which means that only one path to value exists, and therefore, values can't be actually compared. Appears when for example some property exists only in one object and doesn't exist in another.

CollectionDifference - Describes the difference between two collections.

MapDifference - Describes the difference between two maps.

Please see the Javadoc of the Configuration class and it's sub configurations Configuration.Bean and Configuration.Bean.Property to find more methods to configure the comparison process.

Updated