Why is Mokito giving me a new copy of the map?

I have a simple POJO that contains a property Map<String,Widget>

:

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);
}

      

Simple enough: it just doesn't allow duplication Widget

from being inserted into the card. When I go to test this with a JUnit method using Mockito (1.9.5):

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());

      

I am getting the test failing with the following JUnit output:

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

      

And in the console output (STDOUT) I see the following:

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

      

So it looks like the layout that Mockito returns allows for a second (duplicate) insert to be inserted. However, when I remove Mockito completely mockMap

and create a test method:

CachingWidgetFactory fixture = new CachingWidgetFactory();

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

      

Then on the console output I get:

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

      

Now (not mocked) code correctly prevents duplicate insertion. So it's almost like Mockito returns a new HashMap

one every time it is called newWidget

. What's going on here and why? (And bonus points if you can help me with the common problem mentioned above.) Thanks in advance.

+3


source to share


2 answers


This is a common mistake. You must remember that you were bullied HashMap

. So mockito doesn't give you a new map every time, it just doesn't know how to behave because you mocked the HashMap.

By granting it, he will behave as you tell him to behave. If you didn't say anything, it will return the defaults / do nothing when you call your methods. So on the line

 if (!widgetCache.containsKey(name))

      

because you didn't say how it should behave, it will return the default false

. You can mock the card to return false on the second call using Mockito with something like

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

      



The HashMap will return that "contains" the key in the second call containsKey

with the given name. You can read its documentation here

Another thing you could do is give it a real HashMap implementation, but I prefer "mock" :)

EDIT

I gave a link to BDDMockito, but it works the same way with when

thenReturn

. It's just sugar syntax :) I find it easier to read.

+2


source


// How do I enforce generics here when defining a mock?
HashMap mockMap = Mockito.mock(HashMap.class);

      

Handling generics is much easier if you use mockito annotations for example.

@Mock
HashMap<String, Widget> mockMap;

      

and then in your setup method:



MockitoAnnotations.init(this);

      

But to your problem, just use

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

      

so the test is using a real HashMap that behaves like one as this is what you want to test. If you are going to mock all the HashMap operations yourself, chances are you are making some mistake (forgetting to mock the method, for example).

0


source







All Articles