Why is Mockito giving me a new map instance? - java

I have a simple POJO that contains a Map<String,Widget> property:
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 duplicate Widgets from being inserted into the map. When I go to test this in a JUnit test 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 get the test failing with the following JUnit output:
org.mockito.exceptions.verification.TooManyActualInvocations:
hashMap.put(<any>, <any>);
Wanted 1 time:
And in the (STDOUT) console output, I see this:
Printing...0
I don't contain Widget-A
Printing...0
I don't contain Widget-A
So it looks like the mock that Mockito is returning is allowing the 2nd (duplicate) insert. However, when I remove the Mockito mockMap entirely, and make the test method look like:
CachingWidgetFactory fixture = new CachingWidgetFactory();
fixture.newWidget("Widget-A", new Widget("Widget-A"));
fixture.newWidget("Widget-A", new Widget("Widget-A"));
Then in the console output, I get:
Printing...0
I don't contain Widget-A
Printing...1
Now the (non-mocked) code is correctly preventing the duplicate insert. So it's almost as if Mockito is returning a new HashMap every time newWidget is being called. What's going on here, and why? (And bonus points if you can help me on the generic issue mentioned above.) Thanks in advance.

That's a common mistake. You have to remember that you mocked HashMap. So, mockito is not giving you a new map everytime, it just don't know how to behave because you mocked the HashMap.
Giving that, it will behave as you tell it to behave. If you haven't said anything, it will return default values/do nothing when you call your methods. So, at the line
if (!widgetCache.containsKey(name))
because you didnt say how it should behave, it will return the default value of false. You can mock the map to return false at the second call using Mockito with something like
given(hashMap.containsKey(name)).willReturn(false, true);
With that, HashMap will return that "contains" the key at the second call of containsKey with the given name. You can read its documentation here
Another thing you could do is to give to it a real implementation of HashMap, but I prefer the "mock" way :)
EDIT
I gave the link to BDDMockito, but it works the same way with when->thenReturn. It's just syntax sugar :) I find it easier to read.

// How do I enforce generics here when defining a mock?
HashMap mockMap = Mockito.mock(HashMap.class);
Handling generics is much easier if you use the mockito annotations, e.g.
#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 that the test uses a real HashMap that behaves like one, since that is the thing you want to test. If you're going to mock all the HashMap operations yourself, chances are you are making some kind of mistake (like forgetting to mock a method).

Related

How to return a list when unit testing DynamoDB PaginatedQueryList

I have a query statement which returns a PaginatedQueryList and I assign it to a List. Then I operate on the list.
List<MyClass> entries= mapper.query(MyClass.class, queryExpression);
//Logic to test
Map<String, Map> map = entries.stream()
.collect(Collectors.toMap(MyClass::getA, MyClass::getB));
....
....
I tried to test it like
when(mapper.query(eq(MyClass.class), any(DynamoDBQueryExpression.class)))
.thenReturn((PaginatedQueryList<MyClass>) list);
where list is List<MyClass>(ArrayList) that I construct myself and want to pass it to the main program to test the main logic.
But it throws
java.lang.ClassCastException: java.util.ArrayList cannot be cast to com.amazonaws.services.dynamodbv2.datamodeling.PaginatedQueryList
How can I send the list when it executes mappper.query(..).
The motive is to send the list that I constructed to entries when executing mapper.query(..).
NOTE:
I also tried sending a mocked PaginatedQueryList but when it does entries.stream() I want to send the actual stream.
I tried like
when(paginatedQueryList.stream()).thenReturn(Stream.of(list.get(0), list.get(1)));
It throws
java.lang.IllegalStateException: stream has already been operated upon or closed
at java.util.stream.AbstractPipeline.evaluate(AbstractPipeline.java:229)
NOTE 2:
I use entries.stream() twice in my logic to test
Mockito has some powerful tools for this.
List<MyClass> list = new ArrayList<>();
when(mapper.query(eq(MyClass.class), any(DynamoDBQueryExpression.class)))
.thenReturn(mock(PaginatedQueryList.class, withSettings().defaultAnswer(new ForwardsInvocations(list))));
This will return a mocked PaginatedQueryList which forwards all method calls to list.
I was able to solve the problem by sending mocked PaginatedQueryList and then send list.stream() twice (for each access of entries.stream()).
when(paginatedQueryList.stream()).thenReturn(list.stream()).thenReturn(list.stream());
Any better approaches are welcome.
My answer is a generic solution for mocking PaginatedQueryList in tests (might be useful for googlers, since there's not much answers for this topic).
For my test this worked:
// given
List<UserDevice> expectedDevices = Arrays.asList(generateUserDevice(USER_ID, DEVICE_ID), generateUserDevice(USER_ID, "deviceId2"));
PaginatedQueryList listMock = mock(PaginatedQueryList.class);
when(listMock.listIterator()).thenReturn(expectedDevices.listIterator());
when(mapper.query(eq(UserDevice.class), any(DynamoDBQueryExpression.class))).thenReturn(listMock);
// when
List<UserDevice> devices = dao.findAll(USER_ID);
// then
assertEquals(expectedDevices, devices);
so, as you can see, I mocked the PaginatedQueryList and mocked its listIterator() method.

How Do I mock a DAO Layer using JMockit?

I am trying to mock a DAO layer Interface which looks something like this ...
public interface DummyDAO{
public List<VO> getSearchCriteria(String Id, Integer version) throws Exception;
}
In the implementation part, I run a query, and it fetches me a list of Value objects based on that query. How do I mock a List of VO's coming from a database. Is there a simple way to do this ?. I am trying to encourage my team to use Jmockit but I am still a newbie to this mocking tool. If you need more info please let me know.
Thanks
The answer here can depend partly on how the CUT (code under test) is using the List<VO>. For example, if your code is a RESTful service merely acting as a proxy and returning this List<VO> to the user, without examining the contents or anything, then this would suffice:
#Tested CUT cut;
#Injectable DummyDAO dao;
#Injectable List<VO> res;
#Test
public void testSomething() throws Exception {
new Expectations() {{
dao.getSearchCriteria(anyString, anyInt); result = res;
}};
List<VO> output = cut.methodBeingTested();
// assertions go here, like that output and res are the same
// you can also put Verifications here
}
If you are expecting to do analysis of the VOs then things can get more complex, like for example:
#Tested CUT cut;
#Injectable DummyDAO dao;
#Mocked VO vo;
#Test
public void testSomething() throws Exception {
final List<VO> res = new ArrayList<VO>();
Collections.addAll(res, vo, vo, vo, vo);
new Expectations() {{
dao.getSearchCriteria(anyString, anyInt); result = res;
}};
new Expectations(4) {{
vo.getFoo(); returns(5, 47, 13, -7);
}};
cut.methodBeingTested();
// assertions go here, like that you handled the negative number properly
// you can also put Verifications here
}
Notice here I did not mock the List -- if you are doing more complex operations it's better to stick with a real List and not have to mock all of its operations. I mock a single VO and add it to the list multiple times--this is OK because each time you examine it it will act differently and thus seem to be a different VO.
The examination, because it is happening 4 times, I moved to a different Expectations block since you are expecting it to happen 4 times. I'm just doing this to show off the expectations block since in this case it could be just as easily accomplished by having vo.getFoobar(); times=4; returns(5, 47, 13, -7); in the original block... alternatively, this second block could look like
new StrictExpectations(4) {{
vo.getFoo(); returns(5, 47, 13, -7);
vo.getBar(); returns("foo", "bar", "baz", "quux");
}};
In this case, the times field would not work and you'd have to have it in a separate block - this block is saying it expects to have getFoo() and getBar() called, alternating, exactly 4 times--as you would want to do if iterating through a list and looking at both properties each time.
I hope I have given you food for thought; without knowing more of your specific use case I can't be more specific myself.
One simple way to do that is using Mockito.spy() .The spy method create a proxy of your instance that give to you the opportunity to simulate the behavior of one method.
You could create one list that will be returned when you call your dao method.
Example:
yourDao = spy(new YourDaoImplementation())
doReturn(yourExampleList).when(yourDao).getSearchCriteria(any(String.class), any(Integer.class));
If you really want to test your dao, you could use a light weight data base as HSQLDB

Is there are any potentional problems of creating map inside method and returning view?

Here is simple example.
class A {
public Collection method() {
Map map = new HashMap();
// populating map
return map.values();
}
}
It looks like everything must be ok.
Thats fine. Of course it depends on what you want to achieve with this. You will no longer be able to access the map itself unless you kept a reference to it somewhere.

JMockit - "Missing invocation to mocked type" when mocking System.getProperties()

I am admittedly new to JMockit, but I am for some reason having trouble mocking System.getProperties(). Thanks to the help of following post:
https://stackoverflow.com/questions/25664270/how-can-i-partially-mock-the-system-class-with-jmockit-1-8?lq=1
I can successfully mock System.getProperty() using JMockit 1.12:
#Test
public void testAddSystemProperty_String() {
final String propertyName = "foo";
final String propertyValue = "bar";
new Expectations(System.class) {{
System.getProperty(propertyName);
returns(propertyValue);
}};
assertEquals(propertyValue, System.getProperty(propertyName));
}
But the eerily similar code for mocking getProperties() barfs:
#Test
public void testAddSystemProperty_String() {
final String propertyName = "foo";
final String propertyValue = "bar";
final Properties properties = new Properties();
properties.setProperty(propertyName, propertyValue);
new Expectations(System.class) {{
System.getProperties();
returns(properties);
}};
assertEquals(1, System.getProperties().size());
}
I get the following exception that points to the "returns" method:
Missing invocation to mocked type at this point;
please make sure such invocations appear only after
the declaration of a suitable mock field or parameter
Also, how do I mock both methods at the same time? If I put them in the same Expectations block (with the getProperty() first), then I do not see the exception, but System.getProperties() returns the real system properties, not the mocked ones; though getProperty(propertyName) returns the mocked value. I find that totally wonky behavior.
I see from this post that certain methods cannot be mocked, but System.getProperties() is not on that list:
JMockit NullPointerException on Exceptions block?
I am also finding that a lot of solutions on SO that worked with JMockit 2-3 years ago are totally non-compilable now, so apparently things change a lot.
System.getProperties() is indeed one of the methods excluded from mocking in JMockit 1.12. The exact set of such excluded methods can change in newer versions, as new problematic JRE methods are found.
There is no need to mock System.getProperty(...) or System.getProperties(), though. The System class provides setProperty and setProperties methods which can be used instead.
Hopefully someone else finds this useful:
This can be solved with Mockito / PowerMock (1.5.3).
Note that I am testing a utility that will exhaustively try to find the property value given a list of possible sources. A source could be a system property, an environment variable, a meta-inf services file, a jndi name, a thread local, a file on disk, an ldap, basically anything that can perform a lookup.
#RunWith(PowerMockRunner.class)
#PrepareForTest({ClassThatDirectlyCallsSystemInCaseItIsNestedLikeInMyCase.class})
public class ConfigPropertyBuilderTest {
#Test
public void testAddSystemProperty_String_using_PowerMockito() {
PowerMockito.mockStatic(System.class);
PowerMockito.when(System.getProperty(propertyName)).thenReturn(propertyValue);
PowerMockito.when(System.getProperties()).thenReturn(new Properties() {{
setProperty(propertyName, propertyValue);
}});
// Here is realistic case of calling something that eventually calls System
// new configBuilder().addEnvironmentVariable(propertyName)
// .addSystemProperty(propertyName)
// .getValue();
// Here is simplified case:
assertEquals(1, System.getProperties().size());
assertEquals(propertyValue, System.getProperty(propertyName));
}
}
I could call System.setProperty(), but when you start getting into the other sources, it becomes less clear.
Note that I do not specifically care about the value returned by System.getProperty() either; I simply want to ensure that it is called if the first look up fails.
For example, in the above code snippet, the environment variable does not exist, so System.getProperty() should be called. If the environment variable existed (as it does in the next test case which is not shown), then I want to verify that System.getProperty() was not called because it should have short circuited.
Because of the difficulties in faking out the other sources using real files, real ldap, real APIs, etc, and because I want to verify certain APIs are either called or not called, and because I want to keep the tests looking consistent, I think mocking is the correct methodology (even though I may be trying to mock stuff that is not recommended in order to keep it all looking consistent). Please let me know if you think otherwise.
Also, while I do not understand the difficulties of maintaining these mocking frameworks (especially the cglib based ones), I understand the difficulties do exist and I can appreciate the sort of problems you face. My hat goes off to you all.

How to verify multiple method calls with different params

I have the following method that I wish to verify behaviour on.
public void methodToTest(Exception e, ActionErrors errors) {
...
errors.add("exception.message",
ActionMessageFactory.createErrorMessage(e.toString()));
errors.add("exception.detail",
ActionMessageFactory.createErrorMessage(e.getStackTrace()[0].toString()));
...
}
In my #Test class I was hoping to do something like this to verify that errors.add() is called with "exception.message" and again with "exception.detail"
verify(errors).add(eq("exception.message"), any(ActionError.class));
verify(errors).add(eq("exception.detail"), any(ActionError.class));
however Mockito complains as follows
Argument(s) are different! Wanted:
actionErrors.add(
"exception.message",
<any>
);
Actual invocation has different arguments:
actionErrors.add(
"exception.detail",
org.apache.struts.action.ActionError#38063806
);
How can I tell Mockito to check for both values?
Further reading has led me to try using ArgumentCaptors and the following works, although much more verbose than I would like.
ArgumentCaptor<String> argument = ArgumentCaptor.forClass(String.class);
verify(errors, atLeastOnce()).add(argument.capture(), any(ActionMessage.class));
List<String> values = argument.getAllValues();
assertTrue(values.contains("exception.message"));
assertTrue(values.contains("exception.detail"));
If the order of both add() calls is relevant, you can use InOrder:
InOrder inOrder = inOrder(errors);
inOrder.verify(errors).add(eq("exception.message"), any(ActionError.class));
inOrder.verify(errors).add(eq("exception.detail"), any(ActionError.class));
Try something like this:
verify(errors, times(2))
.add(AdditionalMatchers.or(eq("exception.message"), eq("exception.detail")),
any(ActionError.class));
You can use Mockito.atLeastOnce() which allows Mockito to pass the test even if that mockObject will be called many times.
Mockito.verify(mockObject, Mockito.atLeastOnce()).testMethod(Mockito.eq(1));
Mockito.verify(mockObject, Mockito.atLeastOnce()).testMethod(Mockito.eq(2));
you probably have a problem in your code. Because as a matter of fact you actually write this code:
Map<Character, String> map = mock(Map.class);
map.put('a', "a");
map.put('b', "b");
map.put('c', "c");
verify(map).put(eq('c'), anyString());
verify(map).put(eq('a'), anyString());
verify(map).put(eq('b'), anyString());
Note the first verify is not even in order in regard of the actual invocations.
Also, I would recommand you to actually don't mock types you don't own, eg the struts type.
[EDIT #Brad]
After running Brice's code (above) in my IDE I can see that I have used ActionError instead of ActionMessage, so that is why my verify() was not matching. The error message I initially posted was misleading me into thinking it was the first argument that was not matching. It turns out it was the second argument.
So the answer to my question is
/**
* note that ActionMessageFactory.createErrorMessage() returns ActionMessage
* and ActionError extends ActionMessage
*/
verify(errors).add(eq("exception.message"), any(ActionMessage.class));
verify(errors).add(eq("exception.detail"), any(ActionMessage.class));
OP code is correct (check your Total)
=1= Tell Mokito the total call expectation.
=2= Tell Mokito how many times each parameter combination was expected. (Mokito assumes times(1), if times is omitted).
verify(errors, times(2)).add(any(), any(ActionMessage.class));
verify(errors).add(eq("exception.message"), any());
verify(errors).add(eq("exception.detail"), any());
The OP code is correct; it checks what you need.
Your problem was in your Prod code, which (seems) never called the first arg combination with ActionError arg type. So Mokito correctly complained. However (I agree) the complaint message is confusing for the multiple calls.
SOLUTION: Ensure (first of all) you really called the method precisely 2 times (with any args).
In a similar way to #sendon1928 we can use:
Mockito.times(wantedInvocationCount)
to make sure method was called exact number of times (preferable solution in my opinion). Afterwards, we can call
Mockito.verifyNoMoreInteractions(mock)
To make sure that mock was not used further in any context. Full example:
Mockito.verify(mockObject, Mockito.times(wantedInvocationCount)).testMethod(Mockito.eq(1));
Mockito.verify(mockObject, Mockito.times(wantedInvocationCount)).testMethod(Mockito.eq(2));
Mockito.verifyNoMoreInteractions(mockObject)

Categories

Resources