I'm using Mockito 3.4.6 in unit test, actually, i have integrated Mockito to my unit test and it works well. While, now i need to optimize some unit test, it's a special dependency injection that the injected object doesn't have no-arg constructor, I tried @Spy but it didn't work.
My Test: I tried 1. @Spy; 2. @Spy with setting instance using = getDtInsightApi(); 3. @Spy with @InjectMocks, all of tests are failed. As Mockito docs said, seems it can't work for this case.
@InjectMocks Mockito will try to inject mocks only either by constructor injection, setter injection, or property injection in order and as described below.
Also if only use @Spy, it will throw MockitoException:
org.mockito.exceptions.base.MockitoException:
Failed to release mocks
This should not happen unless you are using a third-part mock maker
...
Caused by: org.mockito.exceptions.base.MockitoException: Unable to initialize @Spy annotated field 'api'.
Please ensure that the type 'DtInsightApi' has a no-arg constructor.
...
Caused by: org.mockito.exceptions.base.MockitoException: Please ensure that the type 'DtInsightApi' has a no-arg constructor.
See my pseudocode as below:
configure class:
@Configuration
public class SdkConfig {
@Resource
private EnvironmentContext environmentContext;
@Bean(name = "api")
public DtInsightApi getApi() {
DtInsightApi.ApiBuilder builder = new DtInsightApi.ApiBuilder()
.setServerUrls("sdkUrls")
return builder.buildApi();
}
}
DtInsightApi class with no public no-arg constructor and get instance by its inner class
public class DtInsightApi {
private String[] serverUrls;
DtInsightApi(String[] serverUrls) {
this.serverUrls = serverUrls;
}
// inner class
public static class ApiBuilder {
String[] serverUrls;
public ApiBuilder() {
}
...code...
public DtInsightApi buildApi() {
return new DtInsightApi(this.serverUrls);
}
}
...code...
}
unit test class:
public Test{
@Autowired
private PendingTestService service;
@Spy
private Api api = getDtInsightApi();
@Mock
private MockService mockService;
@Before
public void setUp() throws Exception {
// open mock
MockitoAnnotations.openMocks(this);
// i use doReturn(...).when() for @Spy object
Mockito.doReturn(mockService).when(api)
.getSlbApiClient(MockService.class);
Mockito.when(mockService.addOrUpdate(any(MockDTO.class)))
.thenReturn(BaseObject.getApiResponseWithSuccess());
}
public DtInsightApi getDtInsightApi () {
return new DtInsightApi.ApiBuilder()
.setServerUrls(new String[]{"localhost:8080"})
.buildApi();
}
@Test
public void testUpdate() {
service.update();
}
}
PendingTestService:
@Service
public class PendingTestService{
@Autowired
DtInsightApi api;
public void update() {
// here mockService isn't the object i mocked
MockService mockService = api.getSlbApiClient(MockService.class);
mockService.update();
}
}
Question: How to mock the DI object DtInsightApi which doesn't have no-arg constructor.
Mockito.mock(DtInsightApi.class). And then you stub all the invoked methods. - naimdjonbuildmethod and return a purely mocked instance ofDtInsightApi. By the way you mention that you tried spying on this but it didn't work. What was the problem you noticed? - akortexMockService mockService = api.getSlbApiClient(MockService.class);and found mockService instance isn't a mockito proxy object. - RollsbeanDtInsightApi api = Mockito.mock(DtInsightApi.class);what should i do to inject it to spring? - RollsbeanDtInsightApi. - naimdjon