Java Mockito@InjectMocks未将@Spy分配给正确的字段
我有一个简单的测试用例,其中包含一个模拟对象的间谍列表,然后将其注入到被测试的类中Java Mockito@InjectMocks未将@Spy分配给正确的字段,java,spring,spring-boot,junit,mockito,Java,Spring,Spring Boot,Junit,Mockito,我有一个简单的测试用例,其中包含一个模拟对象的间谍列表,然后将其注入到被测试的类中 @Spy List<ValidateRule> ruleServices = new ArrayList<>(); @Mock private EvaluateGroupType evaluateGroupType; @Mock private ValidateServiceRule validateServiceRule; @InjectMocks private Validate
@Spy List<ValidateRule> ruleServices = new ArrayList<>();
@Mock
private EvaluateGroupType evaluateGroupType;
@Mock
private ValidateServiceRule validateServiceRule;
@InjectMocks
private ValidateRulesService validateRulesService;
@Before
public void init() throws Exception {
initMocks(this);
}
private List<ValidateRule> ruleServices;
private List<ResponseTo> responseToList = new ArrayList<>();
@Autowired
public ValidateRulesService(List<ValidateRule> ruleServices) {
this.ruleServices = ruleServices;
}
@Spy List ruleServices=new ArrayList();
@嘲弄
私有EvaluateGroupType EvaluateGroupType;
@嘲弄
私有ValidateServiceRule ValidateServiceRule;
@注射模拟
私有ValidateRulesService ValidateRulesService;
@以前
public void init()引发异常{
initMocks(这个);
}
但是,在ValidateRulesService类中,列表被注入到错误的列表中
List<Integer> demonstrationList = new ArrayList<>();
@Autowired
private List<ValidateRule> ruleServices;
List demonstrationList=new ArrayList();
@自动连线
私人名单服务;
我还尝试将其作为using构造函数注入,结果是值被注入了两次
List<Integer> demonstrationList = new ArrayList<>();
final private List<ValidateRule> ruleServices;
@Autowired
public ValidateRulesService(List<ValidateRule> ruleServices) {
this.ruleServices = ruleServices;
}
List demonstrationList=new ArrayList();
最终私人服务清单;
@自动连线
公共ValidateRuleService(列出规则服务){
this.ruleServices=ruleServices;
}
我不希望演示列表在这两种情况下都有任何值。因为根据我在文档中读到的内容,它与rulesService没有相同的名称或类型
我是做错了什么,还是这是莫基托的怪癖?这里有两件事在起作用。首先,泛型在运行时不存在,因此基本上Mockito会同时看到
列表
实例,然后应该选择一个。第二个问题是,您的字段被声明为final
,当注入mock/spies时,Mockito将跳过该字段
对于Mockito 1.x(这是使用Spring boot 1.x时的默认设置),据我所知,您无法更改此行为,因此唯一的解决方案是自己在@SetUp
方法中插入字段:
private ValidateRulesService validateRulesService;
@Spy
private List<ValidateRule> ruleServices = new ArrayList<>();
@Before
public void setUp() {
// Inject the mocks/spies by yourself
validateRulesService = new ValidateRulesService(ruleServices);
}
private ValidateRulesService ValidateRulesService;
@间谍
private List ruleServices=new ArrayList();
@以前
公共作废设置(){
//自己注射模拟/间谍
validateRulesService=新的validateRulesService(规则服务);
}
这一点也在本文中提到
另一方面,在Mockito 2.x中,它将使用与spy/mock相同的字段名对字段进行优先级排序,如您在以下内容中所见:
现场注入;mock将首先按类型解析(如果不管名称如何,都会发生单个类型匹配注入),然后,如果存在多个相同类型的属性,则通过字段名和mock名称的匹配来解析
而且:
注意1:如果您有相同类型(或相同擦除)的字段,最好使用匹配字段命名所有@Mock注释字段,否则Mockito可能会混淆,不会发生注入
但是,请注意,如果使用Mockito 2,在注入mock/spies时,它将忽略final
字段:
同样代表setter或field,它们可以用私有可见性声明,Mockito将通过反射看到它们但是,静态或最终字段将被忽略
(重点是我自己的)
因此,如果您使用Mockito 2.x,如果您删除
final
关键字,它应该正确地注入ruleServices
。我通过修改构造函数注入实现,成功地使它正确地模拟了正确的列表
测试
@Spy
List<ValidateRule> ruleServices = new ArrayList<>();
@Mock
private EvaluateGroupType evaluateGroupType;
@Mock
private ValidateServiceRule validateServiceRule;
private ValidateRulesService validateRulesService;
@Before
public void init() throws Exception {
initMocks(this);
ruleServices = Arrays.asList(evaluateGroupType, validateServiceRule);
validateRulesService = new ValidateRulesService(ruleServices);
}
@Spy
List ruleServices=new ArrayList();
@嘲弄
私有EvaluateGroupType EvaluateGroupType;
@嘲弄
私有ValidateServiceRule ValidateServiceRule;
私有ValidateRulesService ValidateRulesService;
@以前
public void init()引发异常{
initMocks(这个);
ruleServices=Arrays.asList(evaluateGroupType,validateServiceRule);
validateRulesService=新的validateRulesService(规则服务);
}
被测试的班级
@Spy List<ValidateRule> ruleServices = new ArrayList<>();
@Mock
private EvaluateGroupType evaluateGroupType;
@Mock
private ValidateServiceRule validateServiceRule;
@InjectMocks
private ValidateRulesService validateRulesService;
@Before
public void init() throws Exception {
initMocks(this);
}
private List<ValidateRule> ruleServices;
private List<ResponseTo> responseToList = new ArrayList<>();
@Autowired
public ValidateRulesService(List<ValidateRule> ruleServices) {
this.ruleServices = ruleServices;
}
私有列表规则服务;
私有列表响应列表=新建ArrayList();
@自动连线
公共ValidateRuleService(列出规则服务){
this.ruleServices=ruleServices;
}
你为什么要窥探这份名单?模拟和设置还不够?@MaciejKowalski虽然这可能会导致更干净的测试,但可能仍然会导致相同的问题。如果使用模拟,则可以指定要注入的字段的名称。如果模拟列表,则无法将模拟对象添加到该列表中。可以进行设置。。我删除了final,但在使用构造函数注入时,它仍然填充两个列表。在这个测试用例中,第二个列表“DeMonstrationList”根本不应该被模仿。@RobertMason如果您删除了final
关键字并使用了字段注入会怎么样?如果spy和实际字段都命名为ruleService
,那么现在应该可以使用了。在问题的顶部,您将看到,这是我的第一个实现,使用同名的字段注入。但出于某种原因,它似乎想将其设置为DemonstationList@RobertMason关于名称,我还不知道,但是,请注意类型是相同的,都是列表
@RobertMason您是对的,我引用的文档来自Mockito 2.x,而Spring boot 1.x默认使用Mockito 1.x。但是,如果您将mockito-core
版本更改为2.x,它应该可以像我提到的那样工作。我现在在答复中澄清了这一点。