Mockito @InjectMocks未将@Spy分配给正确的字段

时间:2017-12-12 09:49:35

标签: java spring spring-boot junit mockito

我有一个简单的测试案例,带有一个间谍模拟对象列表,然后我将其注入正在测试的类中

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

但是,在 ValidateRulesService 类中,列表被注入错误的列表。

List<Integer> demonstrationList = new ArrayList<>();

@Autowired
private List<ValidateRule> ruleServices;

我还尝试将其作为使用构造函数注入注入,这里的结果是值被注入两次

List<Integer> demonstrationList = new ArrayList<>();

final private List<ValidateRule> ruleServices;

@Autowired
public ValidateRulesService(List<ValidateRule> ruleServices) {
    this.ruleServices = ruleServices;
}

enter image description here

我不希望 DemonstationList 在任何一种情况下都有任何值。因为它没有与 rulesService 具有相同的名称或类型,具体取决于我在@injectmocks的文档中所读到的内容。

我在这里做错了什么,或者这是Mockito的怪癖?

2 个答案:

答案 0 :(得分:2)

这里有两件事情可做。首先,泛型在运行时不存在,所以基本上Mockito看到两个List个实例然后应该选择一个。第二个问题是您的字段被声明为final,Mockito在注入模拟/间谍时会跳过该字段。

使用Mockito 1.x(这是使用Spring启动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);
}

Robert Mason's answer中也提到了这一点。

另一方面,使用Mockito 2.x,它将使用与spy / mock相同的字段名称对字段进行优先级排序,如the documentation中所示:

  

现场注射; mocks将首先按类型解析(如果单个类型匹配注入将发生,无论名称如何),那么,如果有多个相同类型的属性,则通过匹配字段名称和模拟名称。

还有:

  

注1 :如果你有相同类型(或相同的删除)的字段,最好用匹配的字段命名所有@Mock带注释的字段,否则Mockito可能会感到困惑并且注入赢了'发生了。

但是,请注意,如果您使用Mockito 2,它会在注入模拟/间谍时忽略final个字段:

  

同样代表建立者或领域,他们可以通过私人可见性宣布,Mockito将通过反思看到它们。 但是,静态或最终字段将被忽略

(强调是我自己的)

因此,如果您使用Mockito 2.x,如果您删除ruleServices关键字,则应正确注入final

答案 1 :(得分:1)

我设法通过改变构造函数注入实现来正确地模拟正确的列表

测试

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

正在测试的班级

private List<ValidateRule> ruleServices;
private List<ResponseTo> responseToList = new ArrayList<>();

@Autowired
public ValidateRulesService(List<ValidateRule> ruleServices) {
    this.ruleServices = ruleServices;
}