【问题标题】:JUnit Test Constructor Injection of List of Dependencies依赖列表的 JUnit 测试构造函数注入
【发布时间】:2021-10-17 13:14:32
【问题描述】:

我正在尝试为以下类编写测试,其中映射字段通过构造函数注入注入依赖项的参数列表。如何模拟依赖项?

@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 是具有不同实现的接口。

我尝试了什么:

    @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;

出现错误: org.mockito.exceptions.misusing.InjectMocksException: 无法实例化名为“com.demo.robot_factory.service.ComponentInputValidator”类型的名为“componentInputValidator”的@InjectMocks 字段。 您没有在字段声明时提供实例,所以我尝试构建实例。 但是构造函数或初始化块抛出异常:null 为线

faceList.stream().collect(Collectors.toMap(faceInterface -> faceInterface.getCode().name(), Function.identity()));

【问题讨论】:

  • “如何”是什么意思?你用 Mockito 标记了你的问题,那么明显吗?
  • 我不认为我们需要测试bean ComponentInputValidator,如果我们把它当作普通类并用它编写单元测试会更好。然后我们可以传递不同的参数,看看构造函数是否按预期工作。
  • 不知道怎么写mock?你能告诉我怎么做吗?我尝试了几种方法,但收到了空指针异常。我知道我做错了什么,但无法弄清楚。我被这个困住了。
  • 你能发布一些你尝试过的方法吗?你到底是从哪里获得 NPE 的?
  • @ParthManaktala 我想做的是这样的: given(robotComponentStock.getQuantity(faceCode)).willReturn(0); assertThat(componentInputValidator.validateStockAvailability(robotComponent)).isFalse();如何编写 MockBean 和 InjectMocks ?

标签: java spring mockito junit5


【解决方案1】:

您在测试中混合了两个不同的概念@MockBean 是在集成测试中使用的 Spring 注释。它将创建一个 mock,然后 Spring 的正常注入机制会将其注入您的 Bean。
另一方面,@InjectMock 是一个来自 Mockito 的注解,用于单元测试

我可以推荐此主题的博客文章: @Mock vs. @MockBean When Testing Spring Boot Applications

如果您想编写单元测试,我建议将所有 @MockBean 注释替换为 Mockitos @Mock

// same for the other dependencies you want to mock
@Mock
private List<MaterialInterface> materialInterfaceList;

@InjectMocks
private ComponentInputValidator componentInputValidator;

这应该可以修复异常。


查看您的代码,我会为您的测试建议一种完全不同的方法。
我不明白你为什么要首先模拟列表。您不能仅实例化列表并手动构建您的ComponentInputValidator 吗?例如:

@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
}

【讨论】:

  • 1) 我按照您的建议做了,将 MockBean 更改为 Mock(这是错误的,我的错)但这次在 Code faceCode = faceMap.get(face).getCode(); 行出现异常因为地图没有“填充”。 2)我照你说的做了,实例化了除地图人口之外的所有东西。如我所愿,仅模拟了robotComponentStock(robotComponentStock.getQuantity(faceCode)).willReturn(0);它奏效了。非常感谢。但我想解决它以及您的第一个建议,因为我花了很多时间来找出如何去做。
  • 正如我在回答中所写的那样,我真的认为您在那里走错了路。 Mocks 是更改组件行为的绝佳工具,就像您使用 RobotComponentStock 所做的那样。我会声称这不是您想要对所有列表执行的操作。它们只是用于传输数据的变量。您尝试做的基本上与模拟 String 并将模拟传递到函数中相同,尽管您只想传递数据 - 没有意义,恕我直言。
猜你喜欢
  • 2015-05-12
  • 1970-01-01
  • 2011-02-02
  • 2011-05-19
  • 1970-01-01
  • 1970-01-01
  • 2018-06-17
  • 1970-01-01
相关资源
最近更新 更多