I am using Mockito to write a simple unit test.
Then, a function under test:
public class MyService {
public void getData() {
executor.execute(new MyRunnable() {
#Override
doTask() {
MyRestClient client = getRestClient();
Response resp = client.getFromServer();
persist(resp.getData());
}
});
}
}
protected MyRestClient getRestClient() {
return new MyRestClient();
}
My test case, I want to test doTask() has run & resp.getData() is persisted:
#Test
public void testGetData() {
MyService spyService = spy(MyService.getInstance());
// mock client
MyRestClient mockedClient = mock(MyRestClient.class);
mockedClient.setData("testData");
// stub getRestClient() function to return mocked client
when(spyService.getRestClient()).thenReturn(mockedClient);
// SUT
spyService.getData();
// run the Runnable task.
Mockito.doAnswer(new Answer<Object>() {
public Object answer(InvocationOnMock invocation) throws Exception {
Object[] args = invocation.getArguments();
Runnable runnable = (Runnable) args[0];
runnable.doTask();
return null;
}
}).when(executor).execute(Mockito.any(Runnable.class));
...
}
As you see above, I stub the getRestClient() function to return a mocked MyRestClient. However when run the test case, it doesn't stub the getRestClient() but run the real function. Why?
[Edit] following comment and review feedback
A rule of thumb is not to mock the class under test. Also your testing will be much easier if your class under test does not use the new keyword. Instead use Factory classes to create objects. There will be no need to use Mockito.spy() only Mockito.mock().
The fact that the following answer requires significant test setup is telling you that MyService has too much reposibility and needs to be simplified. However for the sake of answering your question directly here is how you can refactor your code to support verifying the call to persist() using Mocks.
MyService accepts in the constructor the objects that you will be mocking in your test setup. Having them passed into the constructor allows your JUnit test case to create the Mocks and keep a reference to them for verification later.
public class MyService {
private MyRunnableFactory runFactory;
private MyRestClientFactory restFactory;
private MyRestDao dao;
// inject constructor arguments
public MyService(MyRunnableFactory runFactory, MyRestClientFactory restFactory, MyRestDao dao) {
this.runFactory = runFactory;
this.restFactory = restFactory;
this.dao = dao;
}
public void getData() {
MyRestClient restClient = restFactory.createInstance();
MyRunnable runner = runFactory.createInstance(restClient, dao);
executor.execute(runner);
}
}
MyRunnable is created so that it can be tested in isolation if required. Again we inject the Mock objects into the constructor. It is tempting to inline Runnables as you have written in your question, however you lose the ability to control the new instance being created within you tests.
public class MyRunnable implements Runnable {
private MyRestClient restClient;
private MyRestDao dao;
public MyRunnable(MyRestClient restClient, MyRestDao dao) {
this.restClient = restClient;
this.dao = dao;
}
public void run() {
Response resp = restClient.getFromServer();
dao.persist(resp.getData());
}
}
MyRestDao is created because this is the class that you want to Verify in your test case. I don't see where persist() is defined in your question so we create a Data Access Object (DAO) to implement it.
public class MyRestDao {
public void persist() {
// save to some repository
}
}
Now let's write the test case that uses the above classes. We want to verify that the persist() method has been called
#RunWith(MockitoJUnitRunner.class)
public class MyServiceTest {
#Mock MyRestDao dao;
#Mock MyRestClient restClient;
#Mock MyRunnableFactory runFactory;
#Mock MyRestClientFactory restFactory;
#Test
public void testPersistIsCalled() {
Response expectedResponse = new Response("some data"); // real implementation, not mocked
MyRunnable runner = new MyRunnable(restClient, dao); // real implementation, not mocked
when(restFactory.createInstance()).thenReturn(restClient);
when(runFactory.createInstance(restClient, dao)).thenReturn(runner);
when(restClient.getFromServer()).thenReturn(expectedResponse);
when(restClient.getData()).thenReturn(myRunnable);
// method under test
MyService service = new MyService(runFactory, restFactory);
service.getData();
verify(dao).persist(expectedResponse.getData());
}
}
Note that this test case is brittle because it is tightly coupled to the actual implementation of the MyService class. Ideally you want tests that don't need to know about the internal workings of your class under test.
Related
I have a Helidon application and I would like to test (part of) it.
My test is annotated with #HelidonTest, and now I would like to replace one bean by a mock (and configure this mock, use all other beans as they are found, but with the mock injected).
I did figured out how to:
Replace one bean by a test implementation (separate class): By annotating the test implementation class with #Priority(1) and #Alternative and supply it by annotating the test with #AddBean(MyBeanTestImpl.class).
But I can not create a mock (with Mockito) as an individual class.
Produce a mock(MyBean.class): By creating a producer method and annotate it with #Produces:
But it clashes with the real bean and gives: "WELD-001409: Ambiguous dependencies for type..."
When I annotate it also with #Alternative it is simply ignored.
I can not annotate it with #Priority(1), because this annotation can only be applied to types and parameters.
Any idea how I can replace one bean by a mock?
I tried setter injection to manually inject mock beans.
Class under test
#ApplicationScoped
public class SomeService {
private ExternalService externalService;
#Inject
public void setExternalService(ExternalService externalService) {
this.externalService = externalService;
}
public String doSomething() {
return externalService.getData();
}
}
Test Class
#HelidonTest
class SomeServiceTest {
#Inject
private SomeService someService;
private ExternalService externalService;
#BeforeEach
void setUp() {
externalService = Mockito.mock(ExternalService.class);
someService.setExternalService(externalService);
}
#Test
void doSomething() {
Mockito.when(externalService.getData())
.thenReturn("Mock data");
String actual = someService.doSomething();
Assertions.assertEquals("Mock data", actual);
}
}
There are also methods to mock a whole bean by mocking the constructor as well. For that, we have to make use of #Observes annotation
#HelidonTest
public abstract class HelidonTestHelper {
private MockedConstruction<ExternalService> mockedConstruction;
void init(#Priority(1) #Observes #Initialized(ApplicationScoped.class) ContainerInitialized containerInitialized) {
mockedConstruction = Mockito.mockConstruction(ExternalService.class);
//mock common beans here. This will be executed once application scope is loaded.
}
void onStop(#Priority(1) #Observes #Destroyed(ApplicationScoped.class) ContainerShutdown containerShutdown) {
//do cleanup here if needed.
mockedConstruction.closeOnDemand();
}
}
Once the above is done, instead of helidon test, you can extend the helper class we created.
class SomeServiceTest extends HelidonTestHelper {
#Inject
private SomeService someService;
#Inject //this will be a mock
private ExternalService externalService;
#Test
void doSomething() {
Mockito.when(externalService.getData())
.thenReturn("Mock data");
String actual = someService.doSomething();
Assertions.assertEquals("Mock data", actual);
}
}
adding test cases for getStudent method, this is having internal calls
1- is repository call - stubbing this call working fine
2- validate user call - stubbing this call not working, showing some error and test case failed.
Service Class
#Service
public class StudentServiceImpl implements StudentService {
#Autowired
FakeStudentRepository fakeStudentRepository;
#Override
public Optional<Student> getStudent(int id) {
Optional<Student> student = fakeStudentRepository.getStudent(id);
boolean isValid = myClass().isValidUser(student.get().getId());
if(!isValid) {
return Optional.empty();
}
return student;
}
public MyTestClass myClass() {
return new MyTestClass();
}
}
MyTestClass
public class MyTestClass {
public boolean isValidUser(int id) {
return true;
}
}
Test Class
#SpringBootTest
class StudentServiceImplTest {
#Mock
FakeStudentRepository fakeStudentRepository;
#InjectMocks
StudentServiceImpl studentServiceImpl;
#BeforeEach
public void setup() {
studentServiceImpl = Mockito.spy(StudentServiceImpl.class);
MockitoAnnotations.initMocks(this);
}
#Test
void getStudent() {
Optional<Student> student = Optional.of(Student.builder().id(1).firstName("Rahul").lastName("rahul")
.mobile("XXXXXX").build());
Mockito.doReturn(student)
.when(fakeStudentRepository).getStudent(ArgumentMatchers.anyInt());
Mockito.doReturn(false)
.when(studentServiceImpl).myClass().isValidUser(ArgumentMatchers.anyInt());
Optional<Student> resultStudent = studentServiceImpl.getStudent(student.get().getId());
assertEquals(resultStudent.get().getId(), student.get().getId());
}
}
Error
org.mockito.exceptions.misusing.WrongTypeOfReturnValue: Boolean
cannot be returned by myClass() myClass() should return MyTestClass
If you're unsure why you're getting above error read on. Due to the
nature of the syntax above problem might occur because:
1. This exception might occur in wrongly written multi-threaded tests. Please refer to Mockito FAQ on limitations of concurrency
testing.
2. A spy is stubbed using when(spy.foo()).then() syntax. It is safer to stub spies -
- with doReturn|Throw() family of methods. More in javadocs for Mockito.spy() method.
The error Message says it: You are mocking studentServiceImpl.myClass() and try to return true. It’s not possible to mock the end of a call chain as you try with your second Mockito expression.
To do what you want requires to mock myClass() first by returning a mocked class instance and mock isValidUser on that.
I have a service class, with for readability purpose, I have provided the code with dummy variables and objects. I am trying to write a JUNIT test class for the service, primarily with Mockito. No matter how hard I try, I am not able to hit the method serviceMethod irrespective of using spy/mock. I have also included a test, following the main class.
I know I am missing something here, but doesn't cross my mind. I need an eye to review this and let me know how I can write a proper test class for this and obtain coverage for the method.
(P.S. all the necessary imports are in-place and not pasted here to keep this concise)
Thanks in advance!
#Service
public class ServiceClass {
private static final Logger LOGGER = LoggerFactory.getLogger(ServiceClass.class);
#Autowired
String stringUrl;
RestTemplate restTemplate = new RestTemplate();
public void serviceMethod(ModelObject model) {
try {
HttpEntity<ModelObject> request = new HttpEntity<>(model);
ResponseEntity<String> response = restTemplate.exchange(stringUrl,
HttpMethod.POST, request, String.class);
LOGGER.info(response.getBody() + "and " + response.getStatusCode());
} catch (HttpClientErrorException exception) {
LOGGER.info(exception.getResponseBodyAsString());
} catch (HttpStatusCodeException exception) {
LOGGER.info(exception.getResponseBodyAsString());
}
}
Sample Test:
#RunWith(MockitoJUnitRunner.Silent.class)
public class ServiceClassTest {
#InjectMocks
private ServiceClass serviceClass;
#Mock
private RestTemplate restTemplate;
#Test
public void testServiceMethod() {
ServiceClass spy = Mockito.spy(serviceClass);
// ServiceClass spy = mock(ServiceClass.class, Mockito.CALLS_REAL_METHODS);
doNothing().when(spy).serviceMethod(Mockito.any(ModelObject.class));
Mockito.doCallRealMethod().when(spy).serviceMethod(Mockito.any(ModelObject.class));
ResponseEntity<String> responseEntity = new ResponseEntity<>(HttpStatus.ACCEPTED);
Mockito.when(restTemplate.exchange(ArgumentMatchers.anyString(), ArgumentMatchers.any(HttpMethod.class),
ArgumentMatchers.<HttpEntity<ModelObject>>any(), ArgumentMatchers.<Class<String>>any()))
.thenReturn(responseEntity);
}
In my javaee project there is an interface:
public interface SomeInterface{...}
and multiple implementations:
#Stateless(name = "ImplementationA")
public class ImplementationA implements SomeInterface{...}
#Stateless(name = "ImplementationB")
public class ImplementationB implements SomeInterface{...}
In order to access all of the implementations, I have the following in an other class:
#Singelton
public class AnotherClass{
#Inject
#Any
private Instance<SomeInterface> impls;
public SomeInterface someMethod(){
for(SomeInterface imp : impls){
if(imp.oneMethod()){
return imp;
}
}
return null;
}
}
If I want to do unit test for this "AnotherClass", how do I mock the
Instance<SomeInterface> impls
field?
Tried #Mock, #Spy, could not get "impls" properly mocked from within Mockito, when the test runs, the "impls" is always null.
The Unit test itself looks like the following:
#RunWith(MockitoJUnitRunner.class)
public class SomeTestClass {
#InjectMocks
AnotherClass anotherClass;
#Spy // Here I tried #Mock as well
private Instance<SomeInterface> impls;
#Test
public void TestSomeMethod(){
Assert.assertTrue( anotherClass.someMethod() == null ); // <- NullPointerException here, which indicates the impls is null instead of anotherClass.
}
}
Had to add another method in that "AnotherClass" to accept an instance of Instance impls, which is created in unit test, which works but is ugly that another irrelevant method has to be added only for the purpose of unit test.
Any idea what the proper way of doing unit test looks like?
Mockito and Junit version:
group: 'junit', name: 'junit', version: '4.12'
group: 'org.mockito', name: 'mockito-core', version:'2.12.0'
Thanks in advance.
What you could try to do:
Add some expectations if you need them. You probably need this impls.xxxx() to call a real method if it is a Spy (guess this is default behavior).
Maybe also try to init mocks first:
#RunWith(MockitoJUnitRunner.class)
public class SomeTestClass {
#InjectMocks
AnotherClass anotherClass;
#Spy
private Instance<SomeInterface> impls;
// init here
#Before
public void initMocks() {
MockitoAnnotations.initMocks(this);
}
#Test
public void TestSomeMethod(){
anotherClass.someMethod(); // <- NullPointerException here, which indicates the impls is null instead of anotherClass.
}
}
This init call needs to be somewhere in the base class or a test runner.
That's weird it does not work without, I guess if you use MockitoJUnitRunner it should work.
UPD:
It's been a long time but I can see there are some new comments so providing additional input.
This is the test that works.
// ImplementationA.oneMethod simply returns TRUE in my case
// ImplementationB.oneMethod simply returns FALSE
#RunWith(MockitoJUnitRunner.class)
public class AnotherClassTest {
#Spy // can be Mock
Instance<SomeInterface> impls;
#InjectMocks
AnotherClass classUnderTest;
#Mock
Iterator<SomeInterface> iterator; // why need it - check below :)
#Test
public void someMethod() {
when(impls.iterator()).thenReturn(iterator);
when(iterator.hasNext()).thenReturn(true).thenReturn(false);
when(iterator.next()).thenReturn(new ImplementationA());
SomeInterface res = classUnderTest.someMethod();
System.out.println("done");
}
}
Where was the problem ? Here:
public SomeInterface someMethod() {
// explanation: For-Each uses iterator
// if we do not mock Instance<SomeInterface> impls properly
// impls.iterator() under the hood will return NULL -> NPE
for (SomeInterface imp : impls) {
if (imp.oneMethod()) {
return imp;
}
}
return null;
}
That is why in my test I also create dummy iterator (Mock). I also need to provide some expectations to make it work and here they are:
when(impls.iterator()).thenReturn(iterator); // returns my mock
when(iterator.hasNext()).thenReturn(true).thenReturn(false);
when(iterator.next()).thenReturn(new ImplementationA());
Hope it's clear :) Having this make the for-each works fine and returns ImplementationA.
Happy Hacking :)
I am newe to Mockito and Junit, I have written unit test cases for testing my rest service and made use of Mockito for injecting mocks. And code is below:
BillControllerTest.java:
#RunWith(SpringJUnit4ClassRunner.class)
#SpringApplicationConfiguration(classes = Application.class)
#WebAppConfiguration
public class BillControllerTest{
private MockMvc mockMvc;
#Autowired
private WebApplicationContext webApplicationContext;
#InjectMocks
private BillController billController;
#Mock
private BillService mockBillService;
#Before
public void setupController() {
MockitoAnnotations.initMocks(this);
this.mockMvc = webAppContextSetup(webApplicationContext).build();
}
#Test
public void testBills() throws Exception {
// some fake data
final List<Bill> fakeBillList = new ArrayList<>();
fakeBillList.add(CpsFake.bill("1234"));
when(mockBillService.getBills(BILL_UID))
.thenReturn(fakeBillList.stream());
mockMvc.perform(get("/bills/" + BILL_UID ))
.andExpect(content().contentType(MediaTypes.HAL_JSON))
// expect particular uid
.andExpect(content().string(containsString("\"uid\":\"1234\"")))
ApplicationTest.java:
#RunWith(SpringJUnit4ClassRunner.class)
#SpringApplicationConfiguration(classes = Application.class)
#WebAppConfiguration
public class ApplicationTest {
#Test
public void contextLoads() {
}
}
BillController.java:
#RestController
#RequestMapping(value = "/trials/{billUid}", produces = "application/hal+json")
public class BillController extends BaseController {
#Autowired
private BillService billService;
#Autowired
public BillController(BillService billService) {
this.billService = billService;
}
#RequestMapping(method = RequestMethod.GET, value = "")
public ResponseEntity<Resources<Resource<Bill>>> getBills(#PathVariable String billUid) {
return resourceListResponseEntity(
() -> billService.getBills(billUid),
bill-> createResource(bill),
resources -> resources.add(linkTo(methodOn(BillController.class)
.getBills(billUid)).withSelfRel()));
}
When I run the test (BillControllerTest), mockBillService is not getting invoked and instead it is calling actual billService. Please help me in this issue. Thank you in advance.
I think the problem is that you use mockito together with spring. Both make use of proxys.
Looking at your code of getBills - it is not dependent on the spring application context. So skip all your spring setup code (mockMvc and webApplicationContext) and use only Mockito. If yet invisible code depends on the ApplicationContext - mock the application context rather than setting up a real one.
This test would be:
simpler
container independent
faster
You could replace initMocks with the Annotation RunWith(MockitoJUnitRunner.class) if you want.