3

我有一个包含Map<String,Widget>属性的简单 POJO:

private Map<String, Widget> widgetCache = new HashMap<String, Widget>();

@Override
public Logger newWidget(String name, Widget widget) {
    // First, print the contents of widgetCache in pretty print format.
    Map<String, Widget> map = widgetCache;
    List<String> keys = new ArrayList<String>(map.keySet());
    System.out.println("Printing..." + keys.size());
    for (String key: keys)
        System.out.println(key + ": " + map.get(key).getName());

    if(!widgetCache.containsKey(name)) {
        System.err.println("I don't contain " + name);
        widgetCache.put(name, widget);
    }

    return widgetCache.get(name);
}

很简单:它只是不允许重复Widget的 s 被插入到地图中。当我使用 Mockito (1.9.5) 在 JUnit 测试方法中对此进行测试时:

CachingWidgetFactory fixture = new CachingWidgetFactory();

// By the way, I get errors if I try to make this next line:
//        HashMap<String,Widget> mockMap = Mockito.mock(HashMap<String,Widget>.class);
// How do I enforce generics here when defining a mock?
HashMap mockMap = Mockito.mock(HashMap.class);

fixture.setLoggerCache(mockMap);

fixture.newWidget("Widget-A", new Widget("Widget-A"));
fixture.newWidget("Widget-A", new Widget("Widget-A"));

Mockito.verify(mockMap, Mockito.times(1))
        .put(Mockito.anyString(), Mockito.<Logger>any());

我得到以下 JUnit 输出的测试失败:

org.mockito.exceptions.verification.TooManyActualInvocations: 
hashMap.put(<any>, <any>);
Wanted 1 time:

在(STDOUT)控制台输出中,我看到了:

Printing...0
I don't contain Widget-A
Printing...0
I don't contain Widget-A

因此,看起来 Mockito 返回的模拟允许第二次(重复)插入。但是,当我完全删除 MockitomockMap并使测试方法看起来像:

CachingWidgetFactory fixture = new CachingWidgetFactory();

fixture.newWidget("Widget-A", new Widget("Widget-A"));
fixture.newWidget("Widget-A", new Widget("Widget-A"));

然后在控制台输出中,我得到:

Printing...0
I don't contain Widget-A
Printing...1

现在(非模拟)代码正确地防止了重复插入。因此,就好像 MockitoHashMap每次newWidget被调用时都会返回一个新的。这里发生了什么,为什么?(如果你能在上面提到的一般问题上帮助我,还有加分。)提前致谢。

4

2 回答 2

2

这是一个常见的错误。你必须记住你嘲笑过HashMap。所以,mockito 并不是每次都给你一个新的地图,它只是不知道如何表现,因为你嘲笑了 HashMap。

鉴于此,它会按照您告诉它的行为行事。如果你什么都没说,当你调用你的方法时它会返回默认值/什么都不做。所以,在一线

 if (!widgetCache.containsKey(name))

因为你没有说它应该如何表现,它会返回默认值false. 您可以使用 Mockito 模拟地图以在第二次调用时返回 false ,例如

 given(hashMap.containsKey(name)).willReturn(false, true);

containsKey这样,HashMap 将在第二次调用给定名称时返回“包含”键。你可以在这里阅读它的文档

你可以做的另一件事是给它一个真正的 HashMap 实现,但我更喜欢“模拟”方式:)

编辑

我提供了 BDDMockito 的链接,但它与when->的工作方式相同thenReturn。这只是语法糖 :) 我发现它更容易阅读。

于 2013-02-04T19:45:22.027 回答
0
// How do I enforce generics here when defining a mock?
HashMap mockMap = Mockito.mock(HashMap.class);

如果您使用 mockito 注释,处理泛型会容易得多,例如

@Mock
HashMap<String, Widget> mockMap;

然后在您的设置方法中:

MockitoAnnotations.init(this);

但是对于您的问题,只需使用

HashMap<String, Widget> map=new HashMap<String, Widget>();

以便测试使用行为类似的真实 HashMap,因为这是您要测试的东西。如果您要自己模拟所有 HashMap 操作,那么您很可能会犯某种错误(比如忘记模拟一个方法)。

于 2013-02-04T20:11:09.993 回答