我有一个包含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
。这里发生了什么,为什么? (如果你可以帮助我解决上面提到的一般性问题,还有奖励积分。)先谢谢。
答案 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操作,你可能会犯一些错误(比如忘记模拟一个方法)。