为什么Mockito给我一个新的地图实例?

时间:2013-02-04 19:33:26

标签: java unit-testing generics hashmap mockito

我有一个包含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插入到地图中。当我使用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返回的模拟允许第二次(重复)插入。但是,当我完全删除Mockito mockMap时,使测试方法看起来像:

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

现在(非模拟)代码正确地阻止了重复插入。因此,每次调用HashMap时,Mockito几乎都会返回新的newWidget。这里发生了什么,为什么? (如果你可以帮助我解决上面提到的一般性问题,还有奖励积分。)先谢谢。

2 个答案:

答案 0 :(得分:2)

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

给予它,它会表现得像你告诉它的行为。如果您没有说什么,它会返回默认值/在您调用方法时不执行任何操作。所以,在

 if (!widgetCache.containsKey(name))

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

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

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

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

修改

我提供了指向BDDMockito的链接,但它与when - &gt; thenReturn的工作方式相同。它只是语法糖:)我觉得它更容易阅读。

答案 1 :(得分: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操作,你可能会犯一些错误(比如忘记模拟一个方法)。