Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How to perform deeper matching of keys and values with assertj

Tags:

java

assertj

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?

like image 347
pduncan Avatar asked Jun 17 '14 17:06

pduncan


4 Answers

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"
    );
like image 188
Daniel Käfer Avatar answered Nov 08 '22 06:11

Daniel Käfer


You can also do something like this:

assertThat(characterAges).contains(entry("Frodo", 34), ...);

See https://github.com/joel-costigliola/assertj-core/wiki/New-and-noteworthy#new-map-assertions

like image 36
Clint Harris Avatar answered Nov 08 '22 06:11

Clint Harris


The following methods of AbstractMapAssert will work for you:

  1. containsExactlyEntriesOf Verifies that the actual map contains only the entries of the given map and nothing else, in order. This should be used with TreeMap. HashMap will not guarantee order and your test will randomly fail.

  2. containsExactlyInAnyOrderEntriesOf Verifies that the actual map contains only the given entries and nothing else, in any order. This will work with HashMap.

import java.util.Map;
import org.assertj.core.api.Assertions;
import org.junit.jupiter.api.Test;

public class TestMapAssertions {

    @Test
    public void testCreateMap() {
        //Call
        Map<String, String> actual = ClassUnderTest.createMap();

        //Assert
        Assertions.assertThat(actual).containsExactlyInAnyOrderEntriesOf(
                Map.of("Key1", "Value1", "Key2", "Value2")
        );
    }

    public static class ClassUnderTest {

        public static Map<String, String> createMap() {
            return Map.of("Key1", "Value1", "Key2", "Value2");
        }
    }
}
like image 5
WorkAlcoholic Avatar answered Nov 08 '22 06:11

WorkAlcoholic


Since 3.6.0, you can use hasEntrySatisfying:

 assertThat(characterAges)
            .hasSize(1)
            .hasEntrySatisfying(aKey, e ->
                    assertThat(e)
                            .isEqualTo(99.99)
            );

In your case, if you cannot use the key for lookup you can use Condition-based hasEntrySatisfying (more verbose).

like image 4
laffuste Avatar answered Nov 08 '22 05:11

laffuste