Issue
I'm trying to write tests for the following class, where map fields are being injected through constructor injection with an argument list of dependencies. How can I mock the dependencies?
@Component
public class ComponentInputValidator {
private final Map<String, FaceInterface> faceMap;
private final Map<String, ArmsInterface> armsMap;
private final Map<String, MobilityInterface> mobilityMap;
private final Map<String, MaterialInterface> materialMap;
private final RobotComponentStock robotComponentStock;
public ComponentInputValidator(List<MaterialInterface> materialList,
List<FaceInterface> faceList,
List<ArmsInterface> armsList,
List<MobilityInterface> mobilityList,
RobotComponentStock robotComponentStock){
this.faceMap = faceList.stream().collect(Collectors.toMap(faceInterface -> faceInterface.getCode().name(), Function.identity()));
this.armsMap = armsList.stream().collect(Collectors.toMap(armsInterface -> armsInterface.getCode().name(), Function.identity()));
this.mobilityMap = mobilityList.stream().collect(Collectors.toMap(mobilityInterface -> mobilityInterface.getCode().name(), Function.identity()));
this.materialMap = materialList.stream().collect(Collectors.toMap(materialInterface -> materialInterface.getCode().name(), Function.identity()));
this.robotComponentStock = robotComponentStock;
}
public boolean validateStockAvailability(RobotComponent robotComponent){
String face = robotComponent.getFace();
String arms = robotComponent.getArms();
String mobility = robotComponent.getMobility();
String material = robotComponent.getMaterial();
Code faceCode = faceMap.get(face).getCode();
Code armsCode = armsMap.get(arms).getCode();
Code mobilityCode = mobilityMap.get(mobility).getCode();
Code materialCode = materialMap.get(material).getCode();
if (robotComponentStock.getQuantity(faceCode)<1 ...{
...
return false;
}
return true;
}
}
FaceInterface, ArmsInterface, MobilityInterface, MaterialInterface are interfaces that have different implementations.
What I tried:
@MockBean
private RobotComponentStock robotComponentStock;
@MockBean
private List<FaceInterface> faceInterfaceList;
@MockBean
private List<MobilityInterface> mobilityInterfaceList;
@MockBean
private List<ArmsInterface> armsInterfaceList;
@MockBean
private List<MaterialInterface> materialInterfaceList;
@InjectMocks
private ComponentInputValidator componentInputValidator;
Got an error: org.mockito.exceptions.misusing.InjectMocksException: Cannot instantiate @InjectMocks field named 'componentInputValidator' of type 'class com.demo.robot_factory.service.ComponentInputValidator'. You haven't provided the instance at field declaration so I tried to construct the instance. However the constructor or the initialization block threw an exception : null for the line
faceList.stream().collect(Collectors.toMap(faceInterface -> faceInterface.getCode().name(), Function.identity()));
Solution
You are mixing two different concepts in your test.
@MockBean
is a Spring annotation used in Integration Tests. It will create a mock and Spring's normal injection mechanism will inject it into your Bean.
@InjectMock
on the other hand is an annotation from Mockito used in Unit Tests.
I can recommend this Blog Post on the Subject: @Mock vs. @MockBean When Testing Spring Boot Applications
If you want to write a Unit Test I would suggest swapping all the @MockBean
annotations with Mockitos @Mock
.
// same for the other dependencies you want to mock
@Mock
private List<MaterialInterface> materialInterfaceList;
@InjectMocks
private ComponentInputValidator componentInputValidator;
This should fix the exception.
Looking at your code I would suggest a totally different approach for your test.
I don't understand why you want to mock the Lists in the first place. Can't you just instantiate the Lists and construct your ComponentInputValidator
by hand? For example:
@Test
void test(){
List<MaterialInterface> materialList = List.of(...)
//initialize your other dependencies here
//pass all Lists into the constructor
var validator = new ComponentInputValidator(materialList,...)
//make your assertions on the validator
}
Answered By - Jan Schmitz