albertkao9
albertkao9

Reputation: 504

Make Hamcrest to match a key or value in a Map

I wrote this test program for Hamcrest and Map to use Hamcrest Matchers. This program use Hamcrest as standalone (does not use JUnit). Why none of the output is true (matched)? How to modify this test program so that a key or value in a Map is matched (output is true)?


    import java.util.Map;
    import java.util.stream.Collectors;
    import java.util.stream.Stream;
    
    import org.hamcrest.Matchers;
    import org.hamcrest.collection.IsMapContaining;
    
    public class TestMap {
    
        public static void main(String[] args) {
        
            Map<String, Integer> map = Stream.of(new Object[][] { 
                { "data1", 1 }, 
                { "data2", 2 }, 
            }).collect(Collectors.toMap(data -> (String) data[0], data -> (Integer) data[1]));
    
            for (Map.Entry<String, Integer> entry : map.entrySet()) {
                System.out.println("Matchers.hasKey(\"data1\") " + Matchers.hasKey("data1").matches("data1"));
                System.out.println("Matchers.hasValue(1) " + Matchers.hasValue(1).matches(1));
                System.out.println("IsMapContaining.hasKey(\"data1\") " + IsMapContaining.hasKey("data1").matches("data1"));
                System.out.println("IsMapContaining.hasValue(1) " + IsMapContaining.hasValue(1).matches(1));
                System.out.println("IsMapContaining.hasEntry(entry.getKey(), entry.getValue()) " + IsMapContaining.hasEntry(entry.getKey(), entry.getValue()));
            }
        }
    }

The output is:

Matchers.hasKey("data1") false
Matchers.hasValue(1) false
IsMapContaining.hasKey("data1") false
IsMapContaining.hasValue(1) false
IsMapContaining.hasEntry(entry.getKey(), entry.getValue()) map containing ["data2"-><2>]
Matchers.hasKey("data1") false
Matchers.hasValue(1) false
IsMapContaining.hasKey("data1") false
IsMapContaining.hasValue(1) false
IsMapContaining.hasEntry(entry.getKey(), entry.getValue()) map containing ["data1"-><1>]

Upvotes: 1

Views: 1556

Answers (1)

Eugene
Eugene

Reputation: 120848

I will only explain the first one, you can understand the rest on your own (and btw this is not entirely your fault here).

What do you think happens here:

Matchers.hasKey("data1").matches("data1")

Let's read the documentation of hasKey:

Creates a matcher for java.util.Maps matching when the examined java.util.Map contains at least one key that is equal to the specified key.

Now, let's read the documentation for matches:

This method matches against Object, instead of the generic type T. This is because the caller of the Matcher does not know at runtime what the type is (because of type erasure with Java generics). It is down to the implementations to check the correct type.

Ideally, you want the compiler (javac) to only let you specify a Map in matches (not a "data1" String). You did, after all, say that you want to compare hasKey for a Map, didn't you? But as said in the documentation : "It is down to the implementations to check the correct type", javac can not simply do that (this is a limitation); and you need to do that on your own.

In simpler words, this Matchers.hasKey("data1").matches("data1") should not even compile, but because this is the way javac works (and because hamcrest designed their code like this), this does compile, with a wrong type.

The solution is trivial:

System.out.println("Matchers.hasKey(\"data1\") " + Matchers.hasKey("data1").matches(map));

Notice that matches(map)...

Upvotes: 1

Related Questions