Extract the values of the given field or property from the Iterable's elements under test into a new Iterable, this new
Iterable becoming the Iterable under test.
It allows you to test a property/field of the Iterable's elements instead of testing the elements themselves, which
can be be much less work !
Let's take a look at an example to make things clearer :
// build a list of TolkienCharacters: a TolkienCharacter has a name, and age and a Race (a specific class)
// they can be public field or properties, both can be extracted.
List<TolkienCharacter> fellowshipOfTheRing = new ArrayList<TolkienCharacter>();
fellowshipOfTheRing.add(new TolkienCharacter("Frodo", 33, HOBBIT));
fellowshipOfTheRing.add(new TolkienCharacter("Sam", 38, HOBBIT));
fellowshipOfTheRing.add(new TolkienCharacter("Gandalf", 2020, MAIA));
fellowshipOfTheRing.add(new TolkienCharacter("Legolas", 1000, ELF));
fellowshipOfTheRing.add(new TolkienCharacter("Pippin", 28, HOBBIT));
fellowshipOfTheRing.add(new TolkienCharacter("Gimli", 139, DWARF));
fellowshipOfTheRing.add(new TolkienCharacter("Aragorn", 87, MAN);
fellowshipOfTheRing.add(new TolkienCharacter("Boromir", 37, MAN));
// let's verify the names of the TolkienCharacters in fellowshipOfTheRing :
assertThat(fellowshipOfTheRing).extracting("name")
.contains("Boromir", "Gandalf", "Frodo")
.doesNotContain("Sauron", "Elrond");
// you can extract nested properties/fields like the name of the race :
assertThat(fellowshipOfTheRing).extracting("race.name")
.contains("Hobbit", "Elf")
.doesNotContain("Orc");
A property with the given name is searched for first. If it doesn't exist a field with the given name is looked
for. If the field does not exist an
IntrospectionError is thrown. By default private fields are read but
you can change this with
Assertions#setAllowComparingPrivateFields(boolean). Trying to read a private field
when it's not allowed leads to an
IntrospectionError.
Note that the order of extracted property/field values is consistent with the iteration order of the Iterable under
test, for example if it's a
HashSet, you won't be able to make any assumptions on the extracted values
order.
Extracting also support maps, that is, instead of extracting values from an Object, it extracts maps values
corresponding to the given keys.
Example:
Employee yoda = new Employee(1L, new Name("Yoda"), 800);
Employee luke = new Employee(2L, new Name("Luke"), 22);
Employee han = new Employee(3L, new Name("Han"), 31);
// build two maps
Map<String, Employee> map1 = new HashMap<>();
map1.put("key1", yoda);
map1.put("key2", luke);
Map<String, Employee> map2 = new HashMap<>();
map2.put("key1", yoda);
map2.put("key2", han);
// instead of a list of objects, we have a list of maps
List<Map<String, Employee>> maps = asList(map1, map2);
// extracting a property in that case = get values from maps using the property as a key
assertThat(maps).extracting("key2").containsExactly(luke, han);
assertThat(maps).extracting("key1").containsExactly(yoda, yoda);
// type safe version
assertThat(maps).extracting(key2, Employee.class).containsExactly(luke, han);
// it works with several keys, extracted values being wrapped in a Tuple
assertThat(maps).extracting("key1", "key2").containsExactly(tuple(yoda, luke), tuple(yoda, han));
// unknown keys leads to null (map behavior)
assertThat(maps).extracting("bad key").containsExactly(null, null);