I have Spring class say
@Component
@RequiredArgsConstructor(onConstructor = @__(@Autowired))
class MainServiceImpl implements MainService {
private final InternalService internalService;
public Set<String> do(String anything) {
Set<String> relevent = internalService.finaIntern(anything);
return relevent;
}
}
I am writing Unit Test case as below
@RunWith(MockitoJUnitRunner.class)
class TestMainServiceImpl {
@InjectMocks
private MainServiceImpl service;
@Mock
InternalService internalService;
@Before
public void init(){
MockitoAnnotations.initMocks(this);
}
@Test
public void testDo() {
Set<String> setData = new HashSet<>();
setData.add("ABC");
String a ="a";
when(internalService.finaIntern(any(String.class))
.thenReturn(setData);
Set<String> result = service.do(a);
assertTrue(!result.isEmpty());
}
}
Here my testcase fails , but if I remove final form MainServiceImpl and do an explicit @Autowired like below
@Component
class MainServiceImpl implements MainService {
@Autowired
private InternalService internalService;
.....
Here I am curious to know 1. How does my test case pass if I remove final keyword 2. Is it a good practice to use @RequiredArgsConstructor , if yes then how and if no then also why ?
Thanks in advance
It has nothing to do with lombok
nor Spring @Autowired
The combination of @RunWith(MockitoJUnitRunner.class)
and MockitoAnnotations.initMocks(this);
is the problem. Removing any of it and the behavior is as expected. You don't need both of them. In fact MockitoAnnotations.initMocks(this);
exists only for the cases when you cannot use @RunWith(MockitoJUnitRunner.class)
, for example if you need to use SpringRunner.class
.
Here is why it doesn't work.
First of all of your objects are instantiated. So both your @Mock
is created and injected into you @InjectMock
object:
Below you can see, that the new create mock (mocks[0]
), service
inside of injectInto
and mock
are the same object.
But then the initialization happens the second time.
So mockito create a new @Mock
object, and the tries to inject it into your @InjectMock
object, which is already instantiated. But failed to inject it to the field as long as it's final.
So here is what we have after the second initialization:
As you can see, now the mock object inside of your
testClassInstance
and the mock injected to your Object under test are different.
What about @RequiredArgsConstructor
: for me it's totally ok to use it in the way you did.