I'm writing unit tests for a Spring project with Junit 5 and Mockito 4.
I have to test a class that takes 2 objects via constructor and other 2 via @Autowired. I need to mock those 4 objects, so I annotated them with @Mock in my test class and then annotated the tested class with @InjectMocks.
I thought that @InjectMocks would inject my 4 mocks into myService, but it's only injecting the 2 that are passed by constructor, while the other 2 are null.
I'm looking for a solution that doesn't implies changes in the tested service.
The tested class looks like this:
@Service
public class MyService {
private String key = "KEY";
@Autowired
private FirstApiWrapper firstApiWrapper;
@Autowired
private SecondApiWrapper secondApiWrapper;
private MyRepository myRepository;
private OtherService otherService;
@Autowired
public MyService(
MyRepository myRepository,
OtherService otherService
) {
super();
this.myRepository = myRepository;
this.otherService = otherService;
}
My test class looks like this:
@ExtendWith(MockitoExtension.class)
public class MyServiceTest {
@Mock
MyRepository myRepository;
@Mock
OtherService otherService;
@Mock
FirstApiWrapper firstApiWrapper;
@Mock
SecondApiWrapper secondApiWrapper;
@InjectMocks
MyService myService;
Any ideas of what is wrong with my code? Thank you all very much!
-- I've also tried something based on this question:
@Mock
FirstApiWrapper firstApiWrapper;
@Mock
SecondApiWrapper secondApiWrapper;
@InjectMocks
MyService myService;
@BeforeEach
private void setUp() {
myService = new MyService(
Mockito.mock(MyRepository.class),
Mockito.mock(OtherService.class)
);
}
But the result is exactly the same. Also, if I delete repository and service instances and try to inject only the wrappers, It still fails!
CodePudding user response:
Once of the issues with fields autowiring is that mockito won't be able to inject anything. So why do you need to mix the styles of injection if you already have a constructor-injection?
Rewrite your class:
@Service
public class MyService {
private String key = "KEY";
private final MyRepository myRepository;
private final OtherService otherService;
private final FirstApiWrapper firstApiWrapper;
private final SecondApiWrapper secondApiWrapper;
@Autowired // if its the only constructor in the class, you can omit @Autowired, spring will be able to call it anyway. You can even use Lombok to generate this constructor for, so you won't need to even write this method
public MyService(
MyRepository myRepository,
OtherService otherService,
FirstApiWrapper firstApiWrapper,
SecondApiWrapper secondApiWrapper
) {
this.myRepository = myRepository;
this.otherService = otherService;
this.firstApiWrapper = firstApiWrapper;
this.secondApiWrapper = secondApiWrapper;
}
With this design you can safely use @Mock
/ @InjectMocks
annotations in the test
Mockito will create the instance of the class and inject relevant mocks.
CodePudding user response:
I found a way to solve it without rewriting the existing code, by adding this to the test class:
@BeforeEach
private void setUp() {
MockitoAnnotations.openMocks(this);
}
But I'm not sure if it is a "correct" way of doing it.