web-dev-qa-db-ja.com

MapにエントリのあるMapが含まれているとアサートする方法

ネストされたマップ値をチェックする必要がある単体テストがあります。エントリを引き出して、基になるMapを照合することで、アサーションを機能させることができますが、アサーションの動作を明確に示す方法を探していました。これは非常に簡単なテストです:

_import static org.hamcrest.MatcherAssert.assertThat;
import static org.hamcrest.Matchers.hasEntry;

import Java.util.HashMap;
import Java.util.Map;

import org.junit.Test;

public class MapContainsMapTest {
    @Test
    public void testMapHasMap() {
        Map<String, Object> outerMap = new HashMap<String, Object>();
        Map<String, Object> nestedMap = new HashMap<String, Object>();
        nestedMap.put("foo", "bar");
        outerMap.put("nested", nestedMap);

        // works but murky
        assertThat((Map<String, Object>) outerMap.get("nested"), hasEntry("foo", "bar"));
        // fails but clear
        assertThat(outerMap, hasEntry("nested", hasEntry("foo", "bar")));
    }
}
_

問題は、外側のマップがhasEntry(K key, V value)を使用して比較されているようですが、使用したいのはhasEntry(Matcher<? super K> keyMatcher, Matcher<? super V> valueMatcher)です。 2番目の形式を使用するようにアサーションを強制する方法がわかりません。

前もって感謝します。

10
Jeff E

私はおそらくそのための新しいマッチャーを拡張するでしょう、そのようなものです(潜んでいるNPEに注意してください):

class SubMapMatcher extends BaseMatcher<Map<?,?>> {

private Object key;
private Object subMapKey;
private Object subMapValue;

public SubMapMatcher(Object key, Object subMapKey, Object subMapValue) {
    super();
    this.key = key;
    this.subMapKey = subMapKey;
    this.subMapValue = subMapValue;
}

@Override
public boolean matches(Object item) {

    Map<?,?> map = (Map<?,?>)item;

    if (!map.containsKey(key)) {
        return false;
    }

    Object o = map.get(key);

    if (!(o instanceof Map<?,?>)) {
        return false;
    }

    Map<?,?> subMap = (Map<?,?>)o;
    return subMap.containsKey(subMapKey) && subMap.get(subMapKey).equals(subMapValue);
}

@Override
public void describeTo(Description description) {
    description.appendText(String.format("contains %s -> %s : %s", key, subMapKey, subMapValue));
}

public static SubMapMatcher containsSubMapWithKeyValue(String key, String subMapKey, String subMapValue) {
    return new SubMapMatcher(key, subMapKey, subMapValue);
}

}
4
Florian Schaetz

Map<String, Object>outerMapの値として、それに応じて宣言を調整します。その後、あなたは行うことができます

@Test
public void testMapHasMap() {
    Map<String, Map<String, Object>> outerMap = new HashMap<>();
    Map<String, Object> nestedMap = new HashMap<String, Object>();
    nestedMap.put("foo", "bar");
    outerMap.put("nested", nestedMap);

    Object value = "bar";
    assertThat(outerMap, hasEntry(equalTo("nested"), hasEntry("foo", value)));  
}

Object value = "bar";はコンパイル上の理由から必要です。別の方法として、

assertThat(outerMap,
   hasEntry(equalTo("nested"), Matchers.<String, Object> hasEntry("foo", "bar")));
5
eee

OuterMapをMap<String, Map<String, Object>>として宣言する場合、醜いキャストは必要ありません。このような:

public class MapContainsMapTest {

    @Test
    public void testMapHasMap() {
        Map<String, Map<String, Object>> outerMap = new HashMap<>();
        Map<String, Object> nestedMap = new HashMap<>();
        nestedMap.put("foo", "bar");
        outerMap.put("nested", nestedMap);

        assertThat(outerMap.get("nested"), hasEntry("foo", "bar"));
    }
}