How to perform deeper matching of keys and values with assertj

pduncan picture pduncan · Jun 17, 2014 · Viewed 10.8k times · Source

Say I have a class like this:

public class Character {
   public Character(String name){
      this.name = name;
   }
   private String name;
   public String getName() { return name; }
}

And later, a Map

Map<Character, Integer> characterAges = new HashMap<Character, Integer>();
characterAges.put(new Character("Frodo"), 34);

Using assertj, what is the best way to test that characterAges includes the "Frodo" character? For the age, I can do:

assertThat(characterAges).hasValue(34);

And I know I could do:

assertThat(characterAges.keySet())
               .extracting("name")
               .contains("Frodo");

But then I lose my fluency. What I really want it something like this:

assertThat(characterAges)
               .hasKey(key.extracting("name").contains("Frodo")
               .hasValue(34);

Or even better, so that I can make sure my key and value match:

assertThat(characterAges)
               .hasEntry(key.extracting("name").contains("Frodo"), 34);

Is something like this possible?

Answer

Daniel K&#228;fer picture Daniel Käfer · Feb 1, 2015

There is no easy solution for this. One way is to implement a custom Assertion for the character map. Here is a simple custom Assertion example for this problem:

public class CharacterMapAssert extends AbstractMapAssert<MapAssert<Character, Integer>, Map<Character, Integer>, Character, Integer> {

    public CharacterMapAssert(Map<Character, Integer> actual) {
        super(actual, CharacterMapAssert.class);
    }

    public static CharacterMapAssert assertThat(Map<Character, Integer> actual) {
        return new CharacterMapAssert(actual);
    }

    public CharacterMapAssert hasNameWithAge(String name, int age) {
        isNotNull();

        for (Map.Entry<Character, Integer> entrySet : actual.entrySet()) {
            if (entrySet.getKey().getName().contains(name) && (int) entrySet.getValue() == age) {
                return this;
            }
        }

        String msg = String.format("entry with name %s and age %s does not exist", name, age);
        throw new AssertionError(msg);
    }

}

In the test case:

assertThat(characterAges).hasNameWithAge("Frodo", 34);

Be aware that you have for every custom data structure to write your own assertion. For you Character class you can generate a assertion with the AssertJ assertions generator.


Update Java 8

With Java 8 can also the Lambda Expression used

    assertThat(characterAges).matches(
            (Map<Character, Integer> t)
            -> t.entrySet().stream().anyMatch((Map.Entry<Character, Integer> t1)
                    -> "Frodo".equals(t1.getKey().getName()) && 34 == t1.getValue()),
            "is Frodo and 34 years old"
    );