Testing with functions passed to a mock object - java

I have a simple function which was recently wrapped by some additional logic. I am struggling with updating the test logic since suddently, the method body is wrapped in a mock.
Let me give you an example.
Former logic & test:
// logic
public void doSomething(Transaction t, int a) {
myService.foo(t, a);
}
And my test:
// test
TestedService service;
#Mock
MyService myService;
#Mock
Transaction t;
#Test
public void testSomething() {
testedService.doSomething(t, 10);
Mockito.verify(myService).foo(t, 10);
}
What happened is that we wrapped our logic in some additional function:
public void doSomething(Transaction t, int a) {
model.runInEnhancedTransaction(t, t2 -> { myService.foo(t2, a) });
}
My question is, how do I test this when the logic is suddently wrapped in in model method (model is a mock in my test).
I basically need to verify that t2 -> { myService.foo(t2, a) } was called when the model object is a mock.
EDIT: I made it work with implementing my custom version of model just for the test purpose but still wonder if there is some more elegant way.

It's kind of tough to test these kinds of lambda invocations. What I do is perform two tests: one that model.runInEnhancedTransaction() was called, and one for model.runInEnhancedTransaction() itself. eg
#Test
void doSomethingCallsModelEnhancedTransaction() {
testedService.doSomething(t, 10);
verify(model).runInEnhancedTransaction(eq(t), any());
}
#Test
void modelRunInEnhancedTransaction() {
Transaction t = mock(Transaction.class);
BiConsumer<Transaction, Integer> consumer = mock(Biconsumer.class);
model.runInEnhancedTransaction(t, consumer);
verify(consumer).accept(...);
}

Related

How to mock void method using EasyMock and then how to test it using assert?

I need to unit test a function, which makes an inner call of another void method.
Class TestClass {
public void testMethod() {
someOtherClass.testMethod(); // This is void method
}
}
I need to mock someOtherClass.testMethod() and then verify testMethod of TestClass using assert.
Sorry for my post if it is confusing. Let me make it more clear. My intention is -
public void testMethodTest() {
TestClass tC = new TestClass(); SomeOtherClass obj = EasyMock.createNiceMock(SomeOtherClass.class);
tC.set(obj);
obj.testMethod();
EasyMock.expectLastCall().andAnswer(new IAnswer() {
public Object answer() { // return the value to be returned by the method (null for void)
return null;
}
});
EasyMock.replay(obj);
tC.testMethod(); // How to verify this using assert.
}
What you wrote is working. However, it is overly complicated and you are not verifying that the void method was actually called. To do that, you need to add EasyMock.verify(obj); at the end.
Then, one important thing is that if you call a void method before the replay, it records a call. No need to add an expectLastCall. Also, you could have used expectLastCall().andVoid() instead of the IAnswer.
Here is how I would write it:
#Test
public void testMethodTest() {
TestClass tC = new TestClass();
SomeOtherClass obj = mock(SomeOtherClass.class); // no need to use a nice mock
tC.set(obj);
obj.testMethod();
replay(obj);
tC.testMethod();
verify(obj); // Verify testMethod was called
}

stubbing a nested private async function with powermock

I have a function with the following flow:
public static void func(
ClassA a, ClassB b) {
a.innerfunc().innerfunc().addListener(
new Listener<IUser>() {
#Override
public void onDataReceived(IUser response) {
mAttrDict = response.toDict();
}
#Override
public void onError(ErrorClass error) {
a.getLogger()
.error("log-message");
}
};
}
I would like to stub the addListener and send a custom dict for testing.
Is it somehow possible with powermock?Thanks
The use of PowerMock is almost ever a surrender to a bad design.
In your case it looks like your code violates "Information hiding/encapsulation" which leads to the *chain wracking" calls a.innerfunc().innerfunc()
You should transform you code so that the object needing to be mocked can be passed to your code under test as an injected dependency that can be created using vanilla Mockito.

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

Java Unit tests using Mockito. Function call within a function

Suppose I have the following class :
public class Math {
public int mult(int a, int b) {
return 4;
}
public int mul (int a, int b) {
return mult(a,b);
}
}
And the following test class :
public class TestMockito {
Math testMath;
#Before
public void create () {
testMath = *mock*(Math.class);
when(testMath.mult(1,2).thenReturn(2);
}
#Test
public void test() {
System.out.println(testMath.mul(1,2));
}
}
Why does mul(1,2) called in test() not use when(testMath.mult(1,2).thenReturn(2); ?
Is there any other way to mock a method being used inside another method that is being tested ?
Cheers
You usually do not mock the code under test (unless it is an abstract class).
You usually mock other classes (the dependencies) your CUT communicates with.
The reason why your test does not work (as you expect) is that the mock is not an object of the real class (which is the reason why we mock it BTW....). It has been derived by the mocking framework not to behave like the original code but like it has been configured for the test.
If you really want the real methods being called in the mock (which is not what you want most of the time) you need to tell mockito that when creating the mock:
mock(ClassToBeMocked.class,Mockito.CALL_REAL_METHODS);

Verify order of mock calls

I need to test if one objects calls other object methods in right order.
Unfortunately it looks like Mockito use some kind of grouping for calls with the same parameters. I prepared example which illustrates this behavior.
public class TestInOrder {
#Test
public void test() {
Foo foo = new Foo();
Bar mockBar = mock(Bar.class);
foo.run(mockBar);
InOrder order = Mockito.inOrder(mockBar);
order.verify(mockBar).A();
order.verify(mockBar).B();
order.verify(mockBar).B();
order.verify(mockBar).A();
order.verify(mockBar).B();
}
}
class Foo {
void run(Bar mockBar) {
mockBar.A();
mockBar.B();
mockBar.B();
mockBar.A();
mockBar.B();
}
}
class Bar {
public void A() {
}
public void B() {
}
}
The result is:
org.mockito.exceptions.verification.VerificationInOrderFailure:
Verification in order failure:
bar.B();
Wanted 1 time:
-> at com.goeuro.pi.provider.busfor.TestInOrder.test(TestInOrder.java:19)
But was 3 times. Undesired invocation:
-> at com.goeuro.pi.provider.busfor.Foo.run(TestInOrder.java:32)
I don't understand why I get this error. Order of calls in test is the same as in method.
The issue is that you expect one invocation on each mock. Instead Mockito will count all the invocation on certain mock and will fail when 2 in a row will happen.
This is the implementation for verify method in InOrderImpl class.
public <T> T verify(T mock) {
return this.verify(mock, VerificationModeFactory.times(1));
}
As you can see, it tells mockito to expect exact one invocation on method.
From JavaDoc:
Allows verifying exact number of invocations.
Use VerificationMode to tell Mockito how to verify you mock. This should help:
InOrder order = Mockito.inOrder(mockBar);
order.verify(mockBar).A();
order.verify(mockBar, times(2)).B();
order.verify(mockBar).A();
order.verify(mockBar).B();

Categories

Resources