I am new to Junit and come across this problem recently. I am not able code write test cases wherever I used CompletableFuture in my code. Like below Java file
Updated
AuditService.java
#Autowired
Executor existingThreadPool;
#Override
public void auditData(List<ErrorDetails> alertList) {
CompletableFuture.runAsync(() -> {
if (alertList.isEmpty())
//privateMethodCall1
else
//privateMethodCall2
}, existingThreadPool);
}
I followed this link and tried below solution still getting NPE for CompletableFuture Like below error.
AuditServiceTest.java
#InjectMock
AuditService auditService;
#Mock
private CompletableFuture<Void> completableFuture = null;
#Before
public void setup() {
MockitoAnnotations.initMocks(this);
completableFuture = CompletableFuture.runAsync(new Runnable() {
#Override
public void run() {}
},Executors.newSingleThreadExecutor());
}
#Test
public void shouldAuditData() {
List<ErrorDetails> alertList = new ArrayList();
auditService.auditData(alertList);
}
ERROR
java.lang.NullPointerException
at java.util.concurrent.CompletableFuture.screenExecutor(CompletableFuture.java:415)
at java.util.concurrent.CompletableFuture.runAsync(CompletableFuture.java:1858)
at com.service.impl.AuditService.auditData(AuditService.java:15)
at com.service.impl.AuditServiceTest.shouldAuditData(AuditServiceTest.java:249)
at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62)
at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
at java.lang.reflect.Method.invoke(Method.java:498)
at org.junit.runners.model.FrameworkMethod$1.runReflectiveCall(FrameworkMethod.java:50)
at org.junit.internal.runners.model.ReflectiveCallable.run(ReflectiveCallable.java:12)
at org.junit.runners.model.FrameworkMethod.invokeExplosively(FrameworkMethod.java:47)
at org.junit.internal.runners.statements.InvokeMethod.evaluate(InvokeMethod.java:17)
at org.junit.internal.runners.statements.RunBefores.evaluate(RunBefores.java:26)
at org.junit.runners.ParentRunner.runLeaf(ParentRunner.java:325)
at org.junit.runners.BlockJUnit4ClassRunner.runChild(BlockJUnit4ClassRunner.java:78)
at org.junit.runners.BlockJUnit4ClassRunner.runChild(BlockJUnit4ClassRunner.java:57)
at org.junit.runners.ParentRunner$3.run(ParentRunner.java:290)
at org.junit.runners.ParentRunner$1.schedule(ParentRunner.java:71)
at org.junit.runners.ParentRunner.runChildren(ParentRunner.java:288)
at org.junit.runners.ParentRunner.access$000(ParentRunner.java:58)
at org.junit.runners.ParentRunner$2.evaluate(ParentRunner.java:268)
at org.junit.runners.ParentRunner.run(ParentRunner.java:363)
at org.eclipse.jdt.internal.junit4.runner.JUnit4TestReference.run(JUnit4TestReference.java:86)
at org.eclipse.jdt.internal.junit.runner.TestExecution.run(TestExecution.java:38)
at org.eclipse.jdt.internal.junit.runner.RemoteTestRunner.runTests(RemoteTestRunner.java:459)
at org.eclipse.jdt.internal.junit.runner.RemoteTestRunner.runTests(RemoteTestRunner.java:678)
at org.eclipse.jdt.internal.junit.runner.RemoteTestRunner.run(RemoteTestRunner.java:382)
at org.eclipse.jdt.internal.junit.runner.RemoteTestRunner.main(RemoteTestRunner.java:192)
You need to test your logic and you don't need to mock the static method CompletableFuture.runAsync(...). So your test should look like normal test with exception that you need to wait some time to be sure that asynchronous code is executed, because it is not executed in the same thread. So for the moment I will give you example that you can use with Thread.sleep() which is not good convention, in additional question you can ask how to avoid usages of Thread.sleep().
import org.junit.Test;
import org.junit.runner.RunWith;
import org.mockito.Mock;
import org.mockito.Mockito;
import org.mockito.junit.MockitoJUnitRunner;
import java.util.ArrayList;
import java.util.List;
import java.util.concurrent.CompletableFuture;
import java.util.concurrent.Executor;
import java.util.concurrent.Executors;
#RunWith(MockitoJUnitRunner.class)
public class AuditServiceTest {
#Mock
Service serviceMock;
#Test
public void shouldAuditData() {
AuditService auditService = new AuditService(serviceMock);
List<Object> alertList = new ArrayList();
auditService.auditData(alertList);
// you can wait with Thread.sleep()
// because the execution is asynchronous
Mockito.verify(serviceMock).method1();
Mockito.verify(serviceMock, Mockito.never()).method2();
}
}
class AuditService {
Executor existingThreadPool = Executors.newSingleThreadExecutor();
Service service;
public AuditService(Service service) {
this.service = service;
}
public void auditData(List<Object> alertList) {
CompletableFuture.runAsync(() -> {
if (alertList.isEmpty()) {
service.method1();
} else {
service.method2();
}
}, existingThreadPool);
}
}
class Service {
public void method1(){};
public void method2(){};
}
in AuditService class , Executor is autowired. that is perfect setup for unit tests. what you have to do is , come up with separate configuration for test and Executor implementation should be a inline executor (you can provide your own implementation which calls runnable.run in the same calling thread).
To do this you can use some implementations provided spring-test.
ex: AbstractJUnit4SpringContextTests
if you dont like to go with spring-test support, now you have injected mock Executor to AuditService. so you can mock the execute method with providing custom stub.Answer and execute the runnable.run.
Mockito.doAnswer(new Answer() {
public Object answer(InvocationOnMock invocation) {
Object[] args = invocation.getArguments();
((Runnable)args[0]).run();
return null; // void method, so return null
}
}).when(executor).execute(Mockito.any(Runnable.class));
Related
I've this project structure:
And I am trying to test a dummy method on the Controller. The funcionality is very simple. You send a String by POST and is returned with a + "123"
CustomerServiceImpl.java
package com.example.demo.service;
import org.springframework.stereotype.Service;
#Service
public class CustomerServiceImpl implements CustomerService {
#Override
public String dummyEndpoint(String str) {
return str + "123";
}
}
CustomerController.java
package com.example.demo.controller;
import com.example.demo.service.CustomerService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.http.HttpStatus;
import org.springframework.web.bind.annotation.*;
#RestController
#RequestMapping("/customers")
public class CustomerController {
#Autowired
private CustomerService customerService;
#PostMapping(value = {"/dummy"})
#ResponseStatus(HttpStatus.OK)
public String postDummy(#RequestBody String str) {
return customerService.dummyEndpoint(str);
}
}
And the controller test class:
CustomerControllerTest.java
package com.example.demo.controller;
import com.example.demo.service.CustomerServiceImpl;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.autoconfigure.web.reactive.WebFluxTest;
import org.springframework.boot.test.mock.mockito.MockBean;
import org.springframework.test.context.junit4.SpringRunner;
import org.springframework.test.web.reactive.server.WebTestClient;
#WebFluxTest(controllers = CustomerController.class)
#RunWith(SpringRunner.class)
public class CustomerControllerTest {
#Autowired
WebTestClient webTestClient;
#MockBean
CustomerServiceImpl customerService;
#Test
public void dummyTest() {
this.webTestClient.post().uri("/customers/dummy")
.syncBody("hello")
.exchange()
.expectStatus().isOk()
.expectBody(String.class)
.value(c -> c.equals("hello123"));
}
}
Then, when I test the exepectSatus().isOk() the test is passed:
#Test
public void dummyTest() {
this.webTestClient.post().uri("/customers/dummy")
.syncBody("hello")
.exchange()
.expectStatus().isOk();
}
But if I add the rest of funcionality I get a NPE on the 'c' lambda variable as a Customer object. I am new doing this kind of testing so I don't know what is happening.
#Test
public void dummyTest() {
this.webTestClient.post().uri("/customers/dummy")
.syncBody("hello")
.exchange()
.expectStatus().isOk()
.expectBody(String.class)
.value(c -> c.equals("hello123"));
}
NPE:
java.lang.NullPointerException
at com.example.demo.controller.CustomerControllerTest.lambda$dummyTest$0(CustomerControllerTest.java:29)
at org.springframework.test.web.reactive.server.DefaultWebTestClient$DefaultBodySpec.lambda$value$3(DefaultWebTestClient.java:407)
at org.springframework.test.web.reactive.server.ExchangeResult.assertWithDiagnostics(ExchangeResult.java:197)
at org.springframework.test.web.reactive.server.DefaultWebTestClient$DefaultBodySpec.value(DefaultWebTestClient.java:407)
at com.example.demo.controller.CustomerControllerTest.dummyTest(CustomerControllerTest.java:29)
at java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
at java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62)
at java.base/jdk.internal.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
at java.base/java.lang.reflect.Method.invoke(Method.java:566)
at org.junit.runners.model.FrameworkMethod$1.runReflectiveCall(FrameworkMethod.java:50)
at org.junit.internal.runners.model.ReflectiveCallable.run(ReflectiveCallable.java:12)
at org.junit.runners.model.FrameworkMethod.invokeExplosively(FrameworkMethod.java:47)
at org.junit.internal.runners.statements.InvokeMethod.evaluate(InvokeMethod.java:17)
at org.springframework.test.context.junit4.statements.RunBeforeTestExecutionCallbacks.evaluate(RunBeforeTestExecutionCallbacks.java:74)
at org.springframework.test.context.junit4.statements.RunAfterTestExecutionCallbacks.evaluate(RunAfterTestExecutionCallbacks.java:84)
at org.springframework.test.context.junit4.statements.RunBeforeTestMethodCallbacks.evaluate(RunBeforeTestMethodCallbacks.java:75)
at org.springframework.test.context.junit4.statements.RunAfterTestMethodCallbacks.evaluate(RunAfterTestMethodCallbacks.java:86)
at org.springframework.test.context.junit4.statements.SpringRepeat.evaluate(SpringRepeat.java:84)
at org.junit.runners.ParentRunner.runLeaf(ParentRunner.java:325)
at org.springframework.test.context.junit4.SpringJUnit4ClassRunner.runChild(SpringJUnit4ClassRunner.java:251)
at org.springframework.test.context.junit4.SpringJUnit4ClassRunner.runChild(SpringJUnit4ClassRunner.java:97)
at org.junit.runners.ParentRunner$3.run(ParentRunner.java:290)
at org.junit.runners.ParentRunner$1.schedule(ParentRunner.java:71)
at org.junit.runners.ParentRunner.runChildren(ParentRunner.java:288)
at org.junit.runners.ParentRunner.access$000(ParentRunner.java:58)
at org.junit.runners.ParentRunner$2.evaluate(ParentRunner.java:268)
at org.springframework.test.context.junit4.statements.RunBeforeTestClassCallbacks.evaluate(RunBeforeTestClassCallbacks.java:61)
at org.springframework.test.context.junit4.statements.RunAfterTestClassCallbacks.evaluate(RunAfterTestClassCallbacks.java:70)
at org.junit.runners.ParentRunner.run(ParentRunner.java:363)
at org.springframework.test.context.junit4.SpringJUnit4ClassRunner.run(SpringJUnit4ClassRunner.java:190)
at org.junit.runner.JUnitCore.run(JUnitCore.java:137)
at com.intellij.junit4.JUnit4IdeaTestRunner.startRunnerWithArgs(JUnit4IdeaTestRunner.java:69)
at com.intellij.rt.junit.IdeaTestRunner$Repeater.startRunnerWithArgs(IdeaTestRunner.java:33)
at com.intellij.rt.junit.JUnitStarter.prepareStreamsAndStart(JUnitStarter.java:221)
at com.intellij.rt.junit.JUnitStarter.main(JUnitStarter.java:54)
The example is mocking CustomerServiceImpl but doesn't stub the customerService.dummyEndpoint() method call.
By default Mockito will return null for a non-stubbed method call. This is why just checking the status passes. Since the value is null, value(c -> c.equals("hello123") will fail with NPE.
You'll need to stub the method call:
when(customerService.dummyEndpoint("hello")).thenReturn("hello123");
Of course this doesn't now test the real service, but it's something that should not be tested in a #WebFluxTest.
Obviously this is a dummy example, but if you want to test the service functionality, you can write a plain unit test that verifies that calling the service method returns what is wanted.
I am not sure if I am testing a void returning method the correct way and also if my class-under-test (cut) requires any change in order to make it 100% testable and bug-proof.
I am seeing NullPointerException while executing the test because loginOperations is not getting set.
Error:
java.lang.NullPointerException
at com.demo.service.LoginService.doLogin(LoginService.java:40)
at com.demo.service.LoginServiceTest.doLogin(LoginServiceTest.java:25)
at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62)
at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
at java.lang.reflect.Method.invoke(Method.java:498)
at org.junit.runners.model.FrameworkMethod$1.runReflectiveCall(FrameworkMethod.java:50)
at org.junit.internal.runners.model.ReflectiveCallable.run(ReflectiveCallable.java:12)
at org.junit.runners.model.FrameworkMethod.invokeExplosively(FrameworkMethod.java:47)
at org.junit.internal.runners.statements.InvokeMethod.evaluate(InvokeMethod.java:17)
at org.junit.runners.ParentRunner.runLeaf(ParentRunner.java:325)
at org.junit.runners.BlockJUnit4ClassRunner.runChild(BlockJUnit4ClassRunner.java:78)
at org.junit.runners.BlockJUnit4ClassRunner.runChild(BlockJUnit4ClassRunner.java:57)
at org.junit.runners.ParentRunner$3.run(ParentRunner.java:290)
at org.junit.runners.ParentRunner$1.schedule(ParentRunner.java:71)
at org.junit.runners.ParentRunner.runChildren(ParentRunner.java:288)
at org.junit.runners.ParentRunner.access$000(ParentRunner.java:58)
at org.junit.runners.ParentRunner$2.evaluate(ParentRunner.java:268)
at org.junit.runners.ParentRunner.run(ParentRunner.java:363)
at org.junit.runner.JUnitCore.run(JUnitCore.java:137)
at com.intellij.junit4.JUnit4IdeaTestRunner.startRunnerWithArgs(JUnit4IdeaTestRunner.java:68)
at com.intellij.rt.execution.junit.IdeaTestRunner$Repeater.startRunnerWithArgs(IdeaTestRunner.java:47)
at com.intellij.rt.execution.junit.JUnitStarter.prepareStreamsAndStart(JUnitStarter.java:242)
at com.intellij.rt.execution.junit.JUnitStarter.main(JUnitStarter.java:70)
LoginService.java
#Service
public class LoginService {
#Autowired
private ILoginOperations loginOperations;
public void doLogin(HttpServletRequest request, String result) {
LoginDTO loginDTO = new LoginDTO(request.getParameter("username"), result);
loginOperations.doLogin(loginDTO);
}
}
LoginServiceTest.java
public class LoginServiceTest {
private LoginService instance = new LoginService();
ILoginOperations loginOperations = Mockito.mock(ILoginOperations.class);
HttpServletRequest request = Mockito.mock(HttpServletRequest.class);
String result = "some string";
#Test
public void doLogin() {
when(request.getParameter("username")).thenReturn("johndoe");
instance.doLogin(request, result); //throws NPE while calling loginOperations.doLogin() because
assertNotNull(instance); //IS THIS THE CORRECT WAY TO TEST A VOID RETURNING METHOD ???
}
}
Now, there are 2 ways to fix the test.
I can fix the class-under-test by adding a setter method for loginOperations class and call that setter method in the test
Change #Test public void doLogin() { to #Test(expected = Exception.class) public void doLogin() {
Not sure which one is the best practice above and why.
Another Question:
Other question that I have is how to assert on a method that returns nothing. There is something like verify() but not sure how to use it.
1.You can fix the test case by adding setter method in LoginService or you can use constructor injection like -
#Autowired
public LoginService(ILoginOperations loginOperations) {
this.loginOperations = loginOperations;
}
Validating exception as #Test(expected = Exception.class) public void doLogin() is certainly not a good idea as doLogin method does not throw exception in normal circumstance.
The better way to test method with void return type is using verification API (example - mockito verification API example). You can also use Mockito's ArgumentCaptor to capture argument and assert state of that argument, along with verification API as -
#RunWith(MockitoJUnitRunner.class)
public class LoginServiceTest {
#Captor
private ArgumentCaptor<LoginDTO> captor;
#Mock
private ILoginOperations loginOperations;
#Mock
private HttpServletRequest mockServletRequest;
#InjectMocks
private LoginService loginService;
#Test
public void validateLogin() {
when(mockServletRequest.getParameter("username")).thenReturn("mock_user_name");
loginService.doLogin(mockServletRequest, "mock_result");
verify(loginOperations).doLogin(captor.capture());
LoginDTO expectedLoginDTO = captor.getValue();
assertThat(expectedLoginDTO.getResult(), is("mock_result"));
assertThat(expectedLoginDTO.getUsername(), is("mock_user_name"));
}
}
There is an excellent article from Martin Fowler about this method of testing - Mocks Aren't Stubs
actually you should create a constructor for your LoginService that gets the ILoginOperations, in this way you can create the LoginService in your test class and pass the mocked ILoginOperations as parameter, all this stuff should be done in a #Before method.
Or you can try with #InjectMocks for your LoginService and have your ILoginOperations annotated as #Mock.
I am new to Mockito. I am aware that spy object will never call original method for doReturn, But in my code, which is not happening
import static org.junit.Assert.assertTrue;
import static org.mockito.Matchers.any;
import static org.mockito.Mockito.spy;
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.mockito.Mockito;
import org.mockito.runners.MockitoJUnitRunner;
class MyClass {
public int method1(){
int x =10;
x = method2(2);
return x;
}
public int method2(int y){
method3();
return 20;
}
public int method3(){
return 30;
}
}
#RunWith(MockitoJUnitRunner.class)
public class Method1Test {
private MyClass myClass = new MyClass();
#Before
public void setup(){}
#Test
public void test01(){
MyClass spyMyClass = spy(myClass);
doReturn(28).when(spyMyClass).method2(any());
int a = spyMyClass.method1();
assertTrue("We did it!!!",a==20);
}
}
Below is the error stacktrace:
java.lang.NullPointerException
at com.Method1Test.test01(Method1Test.java:45)
at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
at sun.reflect.NativeMethodAccessorImpl.invoke(Unknown Source)
at sun.reflect.DelegatingMethodAccessorImpl.invoke(Unknown Source)
at java.lang.reflect.Method.invoke(Unknown Source)
at org.junit.runners.model.FrameworkMethod$1.runReflectiveCall(FrameworkMethod.java:50)
at org.junit.internal.runners.model.ReflectiveCallable.run(ReflectiveCallable.java:12)
at org.junit.runners.model.FrameworkMethod.invokeExplosively(FrameworkMethod.java:47)
at org.junit.internal.runners.statements.InvokeMethod.evaluate(InvokeMethod.java:17)
at org.junit.internal.runners.statements.RunBefores.evaluate(RunBefores.java:26)
at org.junit.runners.ParentRunner.runLeaf(ParentRunner.java:325)
at org.junit.runners.BlockJUnit4ClassRunner.runChild(BlockJUnit4ClassRunner.java:78)
at org.junit.runners.BlockJUnit4ClassRunner.runChild(BlockJUnit4ClassRunner.java:57)
at org.junit.runners.ParentRunner$3.run(ParentRunner.java:290)
at org.junit.runners.ParentRunner$1.schedule(ParentRunner.java:71)
at org.junit.runners.ParentRunner.runChildren(ParentRunner.java:288)
at org.junit.runners.ParentRunner.access$000(ParentRunner.java:58)
at org.junit.runners.ParentRunner$2.evaluate(ParentRunner.java:268)
at org.junit.runners.ParentRunner.run(ParentRunner.java:363)
at org.mockito.internal.runners.JUnit45AndHigherRunnerImpl.run(JUnit45AndHigherRunnerImpl.java:37)
at org.mockito.runners.MockitoJUnitRunner.run(MockitoJUnitRunner.java:62)
at org.eclipse.jdt.internal.junit4.runner.JUnit4TestReference.run(JUnit4TestReference.java:86)
at org.eclipse.jdt.internal.junit.runner.TestExecution.run(TestExecution.java:38)
at org.eclipse.jdt.internal.junit.runner.RemoteTestRunner.runTests(RemoteTestRunner.java:538)
at org.eclipse.jdt.internal.junit.runner.RemoteTestRunner.runTests(RemoteTestRunner.java:760)
at org.eclipse.jdt.internal.junit.runner.RemoteTestRunner.run(RemoteTestRunner.java:460)
at org.eclipse.jdt.internal.junit.runner.RemoteTestRunner.main(RemoteTestRunner.java:206)
org.mockito.exceptions.misusing.InvalidUseOfMatchersException:
Misplaced argument matcher detected here:
-> at com.Method1Test.test01(Method1Test.java:45)
Please someone help me out on above and also provide me fix for above issue.
The issue is caused by a type mismatch on the parameter being passed to method2. That's what this part of the exception message is telling you:
org.mockito.exceptions.misusing.InvalidUseOfMatchersException:
Misplaced argument matcher detected here:
The following test passes:
#Test
public void test01(){
MyClass spyMyClass = spy(myClass);
doReturn(28).when(spyMyClass).method2(anyInt());
int a = spyMyClass.method1();
assertTrue("We did it!!!",a==28);
}
There are two changes here:
Use anyInt() instead of any()
Assert that a==28 because you are telling the stub to return 28
I'm working to unit test my Java app..
My goal is to use Powermock to create a spy on an instance of the BOProcessor class. BOProcessor has a final void method; I will setup my spy to throw an exception when this method is called. I will also be mocking MyDao in this same test, but mocking this class is straightforward. The mocked MyDao will then be passed into an instance of MyDaoService named classUnderTest. I will then make assertions against classUnderTest.
Whenever I try to setup the above scenario, Powermock (or Mockito?) throws an InvalidUseOfMatchersException when I setup the doThrow on my spy. Strangely, this exception is only thrown when the doThrow expectation is followed by a call to classUnderTest. If I remove the later call the classUnderTest, the expectation works fine. Even weirder - classUnderTest doesn't even use the spy that is throwing the error!
This is the entirety of my test code outlined above. To highlight the problem, I've removed all code not directly related. (I've even removed the whole purpose of this test.)
package my.package;
import static org.mockito.Matchers.any;
import static org.mockito.Mockito.doThrow;
import static org.powermock.api.mockito.PowerMockito.mock;
import static org.powermock.api.mockito.PowerMockito.spy;
import org.junit.Test;
public class WhatAmIDoingWrong {
#Test
public void whatAmIDoingWrong() {
MyDao mockedDao = mock(MyDao.class);
BOProcessor processor = new BOProcessor();
BOProcessor mockedProcessor = spy(processor);
MyDaoService classUnderTest = new MyDaoService(mockedDao);
doThrow(new Exception()).when(mockedProcessor).process(any(FakeBusinessObject.class));
classUnderTest.interactWithDao();
}
}
Here is the exception - thrown (ironically) from the doThrow line of my test code - which I'm trying to solve.
org.mockito.exceptions.misusing.InvalidUseOfMatchersException:
Invalid use of argument matchers!
0 matchers expected, 1 recorded:
-> at my.package.WhatAmIDoingWrong.whatAmIDoingWrong(WhatAmIDoingWrong.java:21)
This exception may occur if matchers are combined with raw values:
//incorrect:
someMethod(anyObject(), "raw String");
When using matchers, all arguments have to be provided by matchers.
For example:
//correct:
someMethod(anyObject(), eq("String by matcher"));
For more info see javadoc for Matchers class.
at my.package.MyDaoService.interactWithDao(MyDaoService.java:33)
at my.package.WhatAmIDoingWrong.whatAmIDoingWrong(WhatAmIDoingWrong.java:23)
at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
at sun.reflect.NativeMethodAccessorImpl.invoke(Unknown Source)
at sun.reflect.DelegatingMethodAccessorImpl.invoke(Unknown Source)
at java.lang.reflect.Method.invoke(Unknown Source)
at org.junit.runners.model.FrameworkMethod$1.runReflectiveCall(FrameworkMethod.java:50)
at org.junit.internal.runners.model.ReflectiveCallable.run(ReflectiveCallable.java:12)
at org.junit.runners.model.FrameworkMethod.invokeExplosively(FrameworkMethod.java:47)
at org.junit.internal.runners.statements.InvokeMethod.evaluate(InvokeMethod.java:17)
at org.junit.runners.ParentRunner.runLeaf(ParentRunner.java:325)
at org.junit.runners.BlockJUnit4ClassRunner.runChild(BlockJUnit4ClassRunner.java:78)
at org.junit.runners.BlockJUnit4ClassRunner.runChild(BlockJUnit4ClassRunner.java:57)
at org.junit.runners.ParentRunner$3.run(ParentRunner.java:290)
at org.junit.runners.ParentRunner$1.schedule(ParentRunner.java:71)
at org.junit.runners.ParentRunner.runChildren(ParentRunner.java:288)
at org.junit.runners.ParentRunner.access$000(ParentRunner.java:58)
at org.junit.runners.ParentRunner$2.evaluate(ParentRunner.java:268)
at org.junit.runners.ParentRunner.run(ParentRunner.java:363)
at org.eclipse.jdt.internal.junit4.runner.JUnit4TestReference.run(JUnit4TestReference.java:89)
at org.eclipse.jdt.internal.junit.runner.TestExecution.run(TestExecution.java:41)
at org.eclipse.jdt.internal.junit.runner.RemoteTestRunner.runTests(RemoteTestRunner.java:541)
at org.eclipse.jdt.internal.junit.runner.RemoteTestRunner.runTests(RemoteTestRunner.java:763)
at org.eclipse.jdt.internal.junit.runner.RemoteTestRunner.run(RemoteTestRunner.java:463)
at org.eclipse.jdt.internal.junit.runner.RemoteTestRunner.main(RemoteTestRunner.java:209)
Here are the classes used by my test. To reiterate, the MyDaoService named classUnderTest doesn't even know about the spy of BOProcessor; it only works against the mock of MyDao. But the expectations on the BOProcessor spy only fail if the classUnderTest is called.
public class BOProcessor {
public final void process(FakeBusinessObject bar) {}
}
public class FakeBusinessObject {
}
import java.util.Collections;
import java.util.List;
public class MyDao {
public MyDao() {}
public List<String> getAllData(){
return Collections.emptyList();
}
}
public class MyDaoService {
private MyDao applicationDao;
public MyDaoService(MyDao applicationDao) {
this.applicationDao = applicationDao;
}
public synchronized void interactWithDao() {
applicationDao.getAllData();
}
}
I'm usin JUnit 4.12, Mockito 1.10.19, and Powermock 1.7.4. The project is running Spring 4.3.12RELEASE with spring-test included.
Why is Powermock throwing this exception? Am I not using the any Matcher correctly? Why on earth is this exception only thrown when a later call interacts with a different mock?
Thanks for the help!
It turns out that I was using Spies wrong. Something in the way org.mockito.stubbing.Stubber.when(T mock) is implemented means I cannot set expectations on the Spy the way I wanted to. But a Capture was actually a better fit for my use case anyway.
In the end, my test looked like this:
public class FixedNow{
#Test
public void fixedNow() {
MyDao mockedDao = mock(MyDao.class);
BOProcessor mockedProcessor = mock(BOProcessor.class);
FakeBusinessObject problematicBO = new FakeBusinessObject();
ArgumentCaptor<FakeBusinessObject> fakeBOCaptor = ArgumentCaptor.forClass(FakeBusinessObject.class);
MyDaoService classUnderTest = new MyDaoService(mockedDao, mockedProcessor);
doThrow(new Exception()).when(mockedProcessor).process(eq(problematicBO));
doNothing().when(mockedProcessor).process(fakeBOCaptor.capture());
classUnderTest.interactWithDao();
assertThings(BOCaptor.getValue());
}
}
Thanks for your thoughts!
When I run my unit test I would expect the method thisFails() to retry 3 times and then I would expect to see the recovery logger line printed, but it only tries once and then throws the exception. The output at the bottom is after I run my test.
What am I missing?
Feel free to ignore this section, and jump down to the code. The linter did not think I had sufficient exposition in order to post. I thought that was sufficiently worded to get my question across but for some reason I am not allowed to post this question unless I write more stuff. So here is more stuff, blah blah.
--Spring Boot Application--
package com.example.demo;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.retry.annotation.EnableRetry;
#EnableRetry
#SpringBootApplication
public class DemoApplication {
public static void main(String[] args) {
SpringApplication.run(DemoApplication.class, args);
}
}
--Service--
package com.example.demo;
import lombok.extern.slf4j.Slf4j;
import org.springframework.retry.annotation.Recover;
import org.springframework.retry.annotation.Retryable;
import org.springframework.stereotype.Service;
#Service
#Slf4j
public class MyService {
#Retryable(include = RuntimeException.class)
public int thisFails() {
log.info("Help I am failing");
throw new RuntimeException();
}
#Recover
public int thisRecovers(RuntimeException re) {
log.info("I recovered");
return 0;
}
}
--Test Class--
package com.example.demo;
import static org.junit.Assert.*;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.mockito.InjectMocks;
import org.mockito.runners.MockitoJUnitRunner;
#RunWith(MockitoJUnitRunner.class)
public class MyServiceTest {
#InjectMocks
MyService service;
#Test
public void recovery(){
service.thisFails();
}
}
Output
Class JavaLaunchHelper is implemented in both /Library/Java/JavaVirtualMachines/jdk1.8.0_144.jdk/Contents/Home/bin/java (0x10983b4c0) and /Library/Java/JavaVirtualMachines/jdk1.8.0_144.jdk/Contents/Home/jre/lib/libinstrument.dylib (0x1099034e0). One of the two will be used. Which one is undefined.
12:58:32.067 [main] INFO com.example.demo.MyService - Help I am failing
java.lang.RuntimeException
at com.example.demo.MyService.thisFails(MyService.java:15)
at com.example.demo.MyServiceTest.recovery(MyServiceTest.java:17)
at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62)
at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
at java.lang.reflect.Method.invoke(Method.java:498)
at org.junit.runners.model.FrameworkMethod$1.runReflectiveCall(FrameworkMethod.java:50)
at org.junit.internal.runners.model.ReflectiveCallable.run(ReflectiveCallable.java:12)
at org.junit.runners.model.FrameworkMethod.invokeExplosively(FrameworkMethod.java:47)
at org.junit.internal.runners.statements.InvokeMethod.evaluate(InvokeMethod.java:17)
at org.junit.runners.ParentRunner.runLeaf(ParentRunner.java:325)
at org.junit.runners.BlockJUnit4ClassRunner.runChild(BlockJUnit4ClassRunner.java:78)
at org.junit.runners.BlockJUnit4ClassRunner.runChild(BlockJUnit4ClassRunner.java:57)
at org.junit.runners.ParentRunner$3.run(ParentRunner.java:290)
at org.junit.runners.ParentRunner$1.schedule(ParentRunner.java:71)
at org.junit.runners.ParentRunner.runChildren(ParentRunner.java:288)
at org.junit.runners.ParentRunner.access$000(ParentRunner.java:58)
at org.junit.runners.ParentRunner$2.evaluate(ParentRunner.java:268)
at org.junit.runners.ParentRunner.run(ParentRunner.java:363)
at org.mockito.internal.runners.JUnit45AndHigherRunnerImpl.run(JUnit45AndHigherRunnerImpl.java:37)
at org.mockito.runners.MockitoJUnitRunner.run(MockitoJUnitRunner.java:62)
at org.junit.runner.JUnitCore.run(JUnitCore.java:137)
at com.intellij.junit4.JUnit4IdeaTestRunner.startRunnerWithArgs(JUnit4IdeaTestRunner.java:68)
at com.intellij.rt.execution.junit.IdeaTestRunner$Repeater.startRunnerWithArgs(IdeaTestRunner.java:47)
at com.intellij.rt.execution.junit.JUnitStarter.prepareStreamsAndStart(JUnitStarter.java:242)
at com.intellij.rt.execution.junit.JUnitStarter.main(JUnitStarter.java:70)
Process finished with exit code 255
Spring Retry requires a Spring ApplicationContext; you are using Mockito's #InjectMocks instead of Spring's #Autowired, together with the SpringJunit4ClassRunner (or the newer SpringRUnner) #RunWith.
Since there's no ApplicationContext for the test, there's no retry.