Java 如何断言带有条目的映射包含映射

Java 如何断言带有条目的映射包含映射,java,junit,hamcrest,Java,Junit,Hamcrest,我有一个需要检查嵌套映射值的单元测试。我可以通过拉出条目并匹配底层的映射,让断言正常工作,但我一直在寻找一种清晰的方式来显示断言正在做什么。下面是一个非常简单的测试: import static org.hamcrest.MatcherAssert.assertThat; import static org.hamcrest.Matchers.hasEntry; import java.util.HashMap; import java.util.Map; import org.junit.

我有一个需要检查嵌套映射值的单元测试。我可以通过拉出条目并匹配底层的
映射
,让断言正常工作,但我一直在寻找一种清晰的方式来显示断言正在做什么。下面是一个非常简单的测试:

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")));
    }
}
import static org.hamcrest.matcherasert.assertThat;
导入静态org.hamcrest.Matchers.hasEntry;
导入java.util.HashMap;
导入java.util.Map;
导入org.junit.Test;
公共类MapContainsMapTest{
@试验
public void testMapHasMap(){
Map outerMap=newhashmap();
Map nestedMap=newhashmap();
nestedMap.put(“foo”、“bar”);
outerMap.put(“嵌套”,嵌套映射);
//工作,但阴暗
资产((Map)outerMap.get(“嵌套”)、hasEntry(“foo”、“bar”);
//失败但清晰
资产(outerMap、hasEntry(“嵌套”、hasEntry(“foo”、“bar”));
}
}

问题似乎是外部地图正在使用
hasEntry(K键,V值)
进行比较,而我想使用的是
hasEntry(Matcher我可能会为此扩展一个新的Matcher,类似这样的东西(注意,NPE潜伏):

class subpmatcher扩展BaseMatcher map=(map)项;
如果(!map.containsKey(键)){
返回false;
}
对象o=map.get(键);
如果(!(映射的实例)){
返回false;
}
Map subMap=(Map)o;
返回subMap.containsKey(submpkey)和&subMap.get(submpkey).equals(submpvalue);
}
@凌驾
公共无效说明(说明){
description.appendText(String.format(“包含%s->%s:%s”、键、子pkey、子pvalue));
}
公共静态子PMatcher包含SSUBMapWithKeyValue(字符串键、字符串子PKEY、字符串子PValue){
返回新的subpmatcher(key、submpkey、submpvalue);
}
}

如果您将outerMap声明为
Map
您不需要丑陋的演员阵容。如下所示:

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"));
    }
}
公共类MapContainsMapTest{
@试验
public void testMapHasMap(){
Map outerMap=newhashmap();
Map nestedMap=newhashmap();
nestedMap.put(“foo”、“bar”);
outerMap.put(“嵌套”,嵌套映射);
资产(outerMap.get(“嵌套”)、hasEntry(“foo”、“bar”);
}
}

如果您只想将
Map
作为值放入
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)));  
}

“问题似乎是外部映射正在使用…”
hasEntry(is(“key”)、is(“value”)进行比较)例如,
会导致使用第二个表单。也许您可以使用
equalTo
匹配器来代替
is
,但无论哪种方式,可读性都会超出窗口。问题在于外部测试,所以我需要类似
equalTo(“嵌套”)的东西…
但除非我将外部映射类型更改为
map
@DanGetz,否则这不起作用。如果我删除强制转换,它不会编译,则必须将outerMap声明为map。我的示例是我的测试的简化版本,其中outerMap是一个JSON对象,其中并非所有条目都是映射,因此我必须保持映射声明的编写状态。我的示例是我测试的简化版本,其中outerMap是一个JSON对象,其中并非所有条目都是映射,因此我必须保持映射声明的编写状态。谢谢,这个答案有效。不过,我想看看是否可以扩展它以允许任何级别的嵌套。听起来很简单,您可以简单地使用
对象值,S之类的签名tring…keys
。然后遍历这些键,获取子映射,直到到达最后一个(或者偶然发现一个null或not映射作为您的值)并检查该值。例如
包含value-insubmap(someValue,map0Key,map1Key,map2Key);
当然,我个人不建议对键使用标准映射->值或子映射映射,但更具体的映射也允许具有通用值类型(具有子映射和值的映射实际上是一个树,应该很容易找到)。
@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)));  
}
assertThat(outerMap,
   hasEntry(equalTo("nested"), Matchers.<String, Object> hasEntry("foo", "bar")));