Mockito How to mock void method with output argument? - java

I have a void method "functionVoid" that informs a parameter.
public class MyMotherClass {
#Inject
MyClass2 myClass2
public String motherFunction(){
....
String test = "";
myClass2.functionVoid(test);
if (test.equals("")) {
IllegalArgumentException ile = new IllegalArgumentException(
"Argument is not valid");
logger.throwing(ile);
throw ile;
}
....
}
}
public class MyClass2 {
public void functionVoid(String output_value)
{ ....
output_value = "test";
....
}
}
How do I mock this method in the JUnit method my method "motherFunction"?
In my example, the "test" variable is still empty.
#RunWith(MockitoJUnitRunner.class)
public class MyMotherClassTest {
#Mock
private MyClass2 myClass2 ;
#InjectMock
private final MyMotherClass myMotherClass = new MyMotherClass ();
#Test
public void test(){
myMotherClass.motherFunction();
}
}

If you want to mock the return result of motherFunction then you need not worry about the internal implementation of the method (which ends up calling functionVoid). What you do need to do is provide Mockito with an instruction as to what to do when the method, motherFunction is invoked, this can be achieved via the when clause with syntax;
when(mockedObject.motherFunction()).thenReturn("Any old string");
If that misses the point of what you are attempting to achieve then look at how to mock void methods in the documentation and determine whether the use of doAnswer is applicable here, something like;
doAnswer(new Answer<Void>() {
#Override
public Void answer(InvocationOnMock invocation) throws Throwable {
String output_value = invocation.getArguments()[0];
output_value = "Not blank";
return null;
}
}).when(myClass2).functionVoid(anyString());

If you can change functionVoid() to accept a mutable object as the parameter, then you should be able to achieve what you want.
For example, if you change functionVoid() as follows:
public void functionVoid(StringBuilder output_value)
{ ....
output_value.append("test");
....
}
and invoke it in your motherFunction as follows:
public String motherFunction(){
....
StringBuilder test = new StringBuilder();
myClass2.functionVoid(test);
if (test.toString().equals("")) {
Now modifying OceanLife's answer above, you should be able to do the following:
doAnswer(new Answer<Void>() {
#Override
public Void answer(InvocationOnMock invocation) throws Throwable {
StringBuilder output_value = invocation.getArguments()[0];
output_value.append("Not blank");
return null;
}
}).when(myClass2).functionVoid(any(StringBuilder.class));
Of course, if you can change functionVoid(), you could also just make it return a String instead of void.

In my example, the "test" variable is still empty.
This is not a Mockito problem.
Take a look at this question and especially this answer.
The gist of it is that Java is pass by value (this is explained far better at the links above). Nothing in Mockito or Java will ever be able to make the test var anything other than an empty String. It's an empty String before the method call, and will be an empty String after the call.
You can change an object's state within a method (e.g. adding objects to a collection within a method) and see those changes when you exit the method, but you cannot change what object a var references within a method and expect those changes to "stick" once you exit the method. Strings however, are effectively immutable (no state to change), so you can't even do this.
Thus no modifications to test can be made within that method call.

If you want to check method someMethod(String arg) of object Obj then:
String argument = "";
Mockito.verify(Obj, Mockito.times(1)).someMethod(argument);
Obj has to be Mock or Spy.
This works when you want to check if proper argument was passed to void method.
If your method modifies somehow argument then you should use assertion:
someMethod(StringWrapper wrapper) that changes string.
// given
String argument = "a";
String expected = "a_changed";
String wrapped = new StringWrapper(a);
// when
someMethod(wrapped);
// then
Assert.assertEquals(wrapped.getString(), expected)
I am not sure if this what you were looking for?

Related

Unit testing method that uses callback and SynchronousQueue to track results

I'm using Mockito to test a method that internally makes a networking call and returns a value based on the result of the networking call. This method uses a SynchronousQueue to wait for the result, and the result is set by the callback for the networking call:
HelperClass helperClassObject = new HelperClassObject();
...
public SomeResultCode methodWithNetworkCall() {
SynchronousQueue<SomeResultCode> resultQueue = new SynchronousQueue<>();
// some condition checking code
helperClassObject.makeNetworkCall(new GenericCallback() {
#Override
public void onSuccess(JSONObject response) {
resultQueue.offer(SomeResultCode.SUCCESS);
}
#Override
public void onFailure(VolleyError error) {
resultQueue.offer(SomeResultCode.FAILURE);
}
});
SomeResultCode resultCode = null;
try {
resultCode = resultQueue.poll(1, TimeUnit.MINUTES);
} catch (InterruptedException e) {
}
return resultCode == null ? SomeResultCode.FAILURE : resultCode;
}
In one of my unit test methods I'm trying to verify that SUCCESS is returned upon successful network call. I've tried using ArgumentCaptor and doAnswer to trigger the callback's onSuccess. However, the method is returning FAILURE. I put a breakpoint in the onSuccess, and it looks like when I use the ArgumentCaptor way the onSuccess is triggered AFTER the poll has timed out. When I use the doAnswer way, I see onSuccess called during the setup (doAnswer.when) but not after I actually call the method. What am I doing wrong?
EDIT
Stepping through the code again, it looks like answer is called from within the method I'm testing (i.e. when I call testObject.methodWithNetworkCall during my test), NOT during setup. So it is doing exactly what it is supposed to do: responding with onSuccess. But it is responding with onSuccess BEFORE poll is called. So it seems the problem is not that answer and mocking in general is not working/set up wrong, it is an issue with testing with SynchronousQueue.
Here is my test code:
public class TestClassUnitTest {
TestClass sut;
HelperClass helperClassObject = mock(HelperClass.class);
#Before
public void setup() {
sut = new TestClass();
injectField(sut, "helperClassFieldName", helperClassObject);
}
public void injectField(Object testObject, String fieldName, T mockToInject) {
// some code using reflection to inject the mock object into the test object
}
#Test
public void testMethodWithNetworkCallWithCaptor() {
ArgumentCaptor<GenericCallback> captor = ArgumentCaptor.forClass(GenericCallback.class);
SomeResultCode result = sut.methodWithNetworkcall();
verify(helperClassObject, times(1)).makeNetworkCall(captor.capture());
captor.getValue().onSuccess(new JSONObject());
Assert.assertEquals(SomeResultCode.SUCCESS, result);
}
#Test
public void testMethodWithNetworkCallWithDoAnswer() {
doAnswer(new Answer(){
#Override
public Object answer(InvocationOnMock invocation) throws Throwable {
((GenericCallback)invocation.getArguments()[0]).onSuccess(new JSONObject());
return null;
}
}).when(helperClassObject).makeNetworkCall(any(GenericCallback.class));
SomeResultCode result = sut.methodWithNetworkcall();
Assert.assertEquals(SomeResultCode.SUCCESS, result);
}
}
It looks like you're not replacing your HelperClassObject in your system-under-test, or at least you haven't shown us where you have. The mock returned by Mockito.mock (or #Mock or spy or #Spy) doesn't apply to every instance of the class you pass in; it just creates a single instance. You have to make sure to set the instance (HelperClassObject here) in your system-under-test, possibly by passing it in as a constructor parameter, setting the instance as a field, or setting it using a setter method. If you leave it as new HelperClassObject() as you've shown us, there's no way Mockito will be able to help you.
Your reference to "onSuccess called during the setup (doAnswer.when)" worries me a little bit, because if you've created a mock using Mockito.mock, there should be no reason Mockito would actually call your Answer during setup. This leads me to believe that your HelperClassObject or makeNetworkcall method can't be mocked, possibly from having limited visibility, or because they're marked static or final. Mockito effectively works by writing a custom subclass of the class you're mocking, so make sure the classes and methods you're mocking are public and non-final to ensure they're overridable. (It is possible to mock protected or package-private methods, but certain versions of Mockito have complications with certain code structures. Let's rule that out first.)
After you make sure that the class is mockable and that it's using the mocked HelperClassObject instance you pass in, you'll be able to move forward. You'll want to pursue the doAnswer structure: The ArgumentCaptor version won't work, because if your methodWithNetworkcall blocks and waits for a result, then you'll get a FAILURE return value before you ever get a chance to verify and call your callback. (That explains the timeout.) In other cases where your method-under-test can return first, the ArgumentCaptor solution will be more practical for you.
In this case using doAnswer IS the correct approach. The issue is with the way SynchronousQueue worked: it expects multi-threaded usage of this queue:
A blocking queue in which each insert operation must wait for a corresponding remove operation by another thread, and vice versa.
But in this testing case the test runs on a single thread.
Solution: mock the SynchronousQueue, and use doAnswer to get offer() and poll() to push/pop result onto a LinkedList. In the process, I also moved the SynchrnousQueue local variable resultQueue out of methodWithNetworkCall() and made it an instance member. Updated test code below:
public class TestClassUnitTest {
TestClass sut;
private LinkedList testQueue = new LinkedList();
private SynchronousQueue<SomeResultCode> resultQueueMock = mock(SynchronousQueue.class);
private HelperClass helperClassMock = mock(HelperClass.class);
#Before
public void setup() {
sut = new TestClass();
injectField(sut, "resultQueue", resultQueueMock);
injectField(sut, "helperClassFieldName", helperClassMock);
}
public void injectField(Object testObject, String fieldName, T mockToInject) {
// some code using reflection to inject the mock object into the test object
}
#Test
public void testMethodWithNetworkCallWithDoAnswer() {
doAnswer(new Answer(){
#Override
public Object answer(InvocationOnMock invocation) throws Throwable {
((GenericCallback)invocation.getArguments()[0]).onSuccess(new JSONObject());
return null;
}
}).when(helperClassMock).makeNetworkCall(any(GenericCallback.class));
mockQueue();
SomeResultCode result = sut.methodWithNetworkCall();
Assert.assertEquals(SomeResultCode.SUCCESS, result);
}
private void mockQueue() {
doAnswer(new Answer() {
#Override
public Object answer(InvocationOnMock invocation) throws Throwable {
testQueue.push(((SchedulableJob.Result)invocation.getArguments()[0]));
return true;
}
}).when(resultQueueMock).offer(any());
try {
doAnswer(new Answer() {
#Override
public Object answer(InvocationOnMock invocation) throws Throwable {
if (testQueue.size() > 0) {
return testQueue.pop();
} else {
return null;
}
}
}).when(resultQueueMock).poll(1, TimeUnit.MINUTES);
} catch (InterruptedException e) {
}
}
}

Mockito: using a method in "thenReturn" to return a mock doesn't work

I have encountered what I assume might be a bug with Mockito, but was wondering if anyone else can shed light as to why this test doesn't work.
Basically, I have two objects, like this:
public class FirstObject {
private SecondObject secondObject;
public SecondObject getSecondObject() { return secondObject; }
}
public class SecondObject {
private String name;
public String getName() { return name; }
}
The first object is mocked via annotation and the before method:
#Mock
FirstObject mockedFirstObject;
#Before
public void setup() {
MockitoAnnotations.initMocks(this);
}
The second object is mocked in a method:
public SecondObject setupMockedSecondObject() {
SecondObject secondObject = Mockito.mock(SecondObject.class);
Mockito.when(secondObject.getName()).thenReturn("MockObject");
return secondObject;
}
When thenReturn contains a direct call to this method to setup and obtain a mock of the second object, it fails:
#Test
public void notWorkingTest() {
Mockito.when(mockedFirstObject.getSecondObject()).thenReturn(setupMockedSecondObject());
Assert.assertEquals(mockedFirstObject.getSecondObject().getName(), "MockObject");
}
But, when the mock returned by the same method is assigned to a local variable, which is used in thenReturn, it works:
#Test
public void workingTest() {
SecondObject mockedSecondObject = setupMockedSecondObject();
Mockito.when(mockedFirstObject.getSecondObject()).thenReturn(mockedSecondObject);
Assert.assertEquals(mockedFirstObject.getSecondObject().getName(), "MockObject");
}
Are we doing something wrong or is this indeed a bug/limitation in Mockito? Is there a deliberate reason for this not working?
This is indeed a limitation of Mockito, and it is referenced in their FAQ:
Can I thenReturn() an inlined mock()?
Unfortunately you cannot do this:
when(m.foo()).thenReturn(mock(Foo.class));
// ^
The reason is that detecting unfinished stubbing wouldn't work if we allow above construct. We consider is as a 'trade off' of framework validation (see also previous FAQ entry). However you can slightly change the code to make it working:
//extract local variable and start smiling:
Foo foo = mock(Foo.class);
when(m.foo()).thenReturn(foo);
The workaround, as mentioned, is to store the desired returned value in a local variable, like you have done.
The way I understand it is that Mockito validates the usage you make of it every time you call its methods. When another method is called during an on-going stubbing process, you are breaking its validation process.
You can't use a method in thenReturn, but you can in thenAnswer
Your code will be called after the when condition will occur,
unlike any workaround based on thenReturn
Thus you could write:
#Test
public void nowWorkingTest() {
Mockito.when(mockedFirstObject.getSecondObject()).thenAnswer(new Answer<Map>() {
#Override
public Map answer(InvocationOnMock invocation) {
return setupMockedSecondObject();
}
});
Assert.assertEquals(mockedFirstObject.getSecondObject().getName(), "MockObject");
}
Let find another example here
#Test
public void testAuthenticate_ValidCredentials() throws FailedToAuthenticateException {
String username = "User1";
String password = "Password";
/*Configure Returning True with when...thenReturn configuration on mock Object - Q5*/
//Write your code here
assertTrue(authenticator.authenticateUser(username, password));
}

How to make Mockito throw an exception when a mock is called with non-defined parameters?

Is it possible to throw an exception whenever a mock is called with non-predefined arguments? There is Answers.RETURNS_SMART_NULLS, but it's not really what I need, since it doesn't work if null is legitimate return value, which doesn't lead to NullPointerException, but rather to errors later on.
Edit: some background. So, in Mockito when you define a mock, you specify the return values for each call like this:
when(myMock.someMethod(arg1, arg2)).thenReturn(returnValue);
When myMock.someMethod is called with arguments, for which I didn't give a return value in the test, it just returns null. I would like to configure it to crash right away and tell me that I forgot to define the return value for some combination of parameters.
Edit 2: There were suggestions to provide a custom defaultAnswer that would throw exceptions when called. Unfortunately, this doesn't work. The default answers' answer() method is called even if a mock is present. Here's a sample:
public class Test {
public static class Adder {
public int add(int a, int b) {
return a + b;
}
}
public static final Answer<Object> THROW_ON_UNDEFINED_ARGS = new Answer<Object>() {
#Override
public Object answer(InvocationOnMock invocation) throws Throwable {
throw new IllegalArgumentException(
String.format("Calling a mock with undefined arguments: %s %s",
invocation.getMethod(),
Arrays.toString(invocation.getArguments())));
}
};
public static void main(String[] args) {
Adder adderMock = mock(Adder.class, THROW_ON_UNDEFINED_ARGS);
when(adderMock.add(2, 3)).thenReturn(5);
System.out.println(adderMock.add(2, 3));
}
}
The exception is thrown even though adderMock.add(2, 3) is defined.
You could provide a default Answer in the construction of your mock that always throws an exception. Then every call that is stubbed will act like usual. Everything outside those paths will throw an exception. Something like this:
final String arg = "some arg";
Collection<Object> object = mock(Collection.class, new Answer<Object>() {
#Override
public Object answer(InvocationOnMock invocation) throws Throwable {
throw new IllegalArgumentException("You cannot invoke " + invocation.getMethod() +
" with " + Arrays.toString(invocation.getArguments()));
}
});
doReturn(true).when(object).add(arg);
object.add(arg); // Goes ok
object.add("azertyuiop"); // Throws the exception
Just point another way you can do that, using thenAnswer:
when(myMock.someMethod(anyString(), anyString())).
thenAnswer(new Answer<String>() {
#Override
public String answer(InvocationOnMock invocation) throws Throwable {
Object[] args = invocation.getArguments();
String arg1 = (String) args[0];
String arg2 = (String) args[1];
if ("arg1".equals(arg1) && "arg2".equals(arg2)) return "someValue";
throw new Exception();
}
});
myMock.someMethod("arg1", "arg2"); // Returns "someValue"
myMock.someMethod("xxx", "yyy"); // Throws Exception
Hope it helps.
First, a bit of "good engineering" mumble - why would you like to do this? Mockito tries to 'promote' BDD style - you set up (mock) your calls, you execute the code and verify interactions were exactly as you expected rather then 'it didn't called anything else' - Do you try to do something described in Finding irrelevant invocation?
Generally if I want to mock all the cases, but one - this makes my ask myself whether my tests are really OK.
Anyway, to the topic :)
In Mockito, you can define multiple whens with different values, like
class Foo {
public String bar(int a) {
return "bar = " + a;
}
}
Mockito.when(task.bar(Matchers.anyInt())).thenReturn("L")
Mockito.when(task.bar(3)).thenThrow(new IllegalAccessError())
task.bar(4); // returns "L"
task.bar(3); //throws IllegalAccessError
Notice that the order of whens DOES matter. The rules are processed in reversed order (or rather overrides the actual matchers).
In my code we first mock for anyInt, then for 3 - which works. If you reverse it - both calls to bar() will return 'L'.

Mocking one method with different values

I still have some difficulties with Mockito. I want to have two test cases for two different object examples. So I want to simulate different method behaviour depending on argument value.
The problem is that when I run test() method, the returned value of help valiable is "b" and the assertion doesn't return true. If I comment the line marked as (***), everything works fine.
As you can see I tried to use thenAnswer instead of thenReturn, but the result was the same.
public class TestItAll {
TestClass test;
HelpClass a ;
HelpClass b;
#Before
public void init(){
a = new HelpClass("a");
b = new HelpClass("b");
Mockito.when(test.getHelp(a)).thenReturn("a");
/*Mockito.when(test.getHelp(a)).thenAnswer(
new Answer< String>() {
public String answer(InvocationOnMock invocation) {
return "a";
}
}); */
Mockito.when(test.getHelp(b)).thenReturn("b");//(***)
/*Mockito.when(test.getHelp(b)).thenAnswer(
new Answer< String>() {
public String answer(InvocationOnMock invocation) {
return "b";
}
}); */
}
#Test
public void testA(){
String help= test.getHelp(a);
Assert.assertEquals(help, "a");
}
/*#Test
public void testB(){
String help= test.getHelp(b);
Assert.assertEquals(help, "b");
}*/
}
Please, don't ask me why I'm mocking a test object. It's just a model example of a more complicated situation.
Firstly, I assume that your declaration TestClass test; is in fact TestClass test = mock(TestClass.class);, otherwise the #Before method throws NullPointerException.
When using when(test.getHelp(a)) mockito will use a's equals method to check whether the parameter matched. If e.g. equals method always returns true, it won't be able to differ a from b. I have run your code with overriding equals method (i.e. HelpClass objects are equal only if they are the same instance) and both tests have passed.
You may want to use argument matcher - when(test.getHelp(argThat(sameInstance(a)))) to not rely on your equals method. If you need something more complex than sameInstance, I would recommend sameBeanAs matcher from shazamcrest.

How to rectify junit giving assertion error as expected:1 and actual:0?

I have a junit method like this :
public void testMyStuff() {
IMockBuilder<BackingBean> builder = createMockBuilder(BackingBean.class);
builder.addMockedMethod("getMyFacesContextSessionMap");
BackingBean bean = builder.createMock();
MyVO MyVO = new MyVO();
List<MyVO> MyVOList = new ArrayList<MyVO>();
HttpServletRequest req = createMock(HttpServletRequest.class);
bean.setHttpServletRequest(req);
MyVOList.add(buildMyVO());
expect(bean.getMyFacesContextSessionMap()).andReturn(
new HashMap<String, Object>());
expect(ac.getBean("MyService")).andReturn(service);
expect(MyFacade.getMyStuff(MyVO)).andReturn(MyVOList);
expect(bean.getMyFacesContextSessionMap()).andReturn(
new HashMap<String, Object>());
replay(bean, MyFacade);
bean.setService(service);
String returnString = bean.myStuff();
assertEquals("myNo", returnString);
}
When i run it as junit in eclipse i get this error :
java.lang.AssertionError:
Unexpected method call MyFacadeLocal.getMyStuff(MyVO#8c64cdd8):
MyFacadeLocal.getMyStuff(MyVO#d79a467f): expected: 1, actual: 0
at org.easymock.internal.MockInvocationHandler.invoke(MockInvocationHandler.java:44)
at org.easymock.internal.ObjectMethodsFilter.invoke(ObjectMethodsFilter.java:85)
at $Proxy1.getMyStuff(Unknown Source)
I am not understanding how to rectify this . Can some one help me plz ?
Example of an ArgumentMatcher:
public class CollectionCountMatcher implements IArgumentMatcher
{
private int expectedCount;
public CollectionCountMatcher(int count) {
super();
this.expectedCount = count;
}
public boolean matches(Object actual) {
return (actual instanceof Collection)
&& ((Collection) actual).size() == expectedCount;
}
public static final Integer eqObject(Integer inValue) {
EasyMock.reportMatcher(new CollectionCountMatcher (inValue));
return inValue;
}
public void appendTo(StringBuffer buffer) {
buffer.append("collectionContains(" + expectedCount + ")");
}
}
You create an MyVOMatcher and you call your mock method like this:
expect(MyFacade.getMyStuff(MyVOMatcher.eqObject(MyVO))).andReturn(MyVOList);
EDIT: Or what you can also do, but this way of working is less strict and doesn't check for different objects is doing it like this:
expect(MyFacade.getMyStuff(EasyMock.anyObject(myVO.class))).andReturn(MyVOList);
This will work for sure, but off course you can pass every possible argument, as long as it's of the type MyVO
This means that the method
getMyStuff
with as argument MyVO is never called in your backing bean. Check if that method is really called. If that's the case, implement the equals() and hashCode() methods for MyVO since probably it's not recognized as the same element since it's a different object.
The error message just tells you you expect the method with the EXACT parameter that you specify, but that it wasn't encountered...
I guess it's because getMyStuff is a static method. Mocking static methods is not ideal with EasyMock. Maybe consider Powermock and if you want to do it in EasyMock anyway, you can try the following:
You could move the static call to a method, override this method in the instantiation of the tested class in the test class, create a local interface in the test class and use its method in the overidden method:
private interface IMocker
{
List<MyVO> getMyStuff(MyVO myVO);
}
IMocker imocker = EasyMock.createMock(MyFacade.class);
...
#Override
List<MyVO> getMyStuff(MyVO myVO)
{
imocker.getMyStuff(myVO);
}
...
EasyMock.expect(imocker.getMyStuff(EasyMock.anyObject(myVO.class))).andReturn(true);

Categories

Resources