【问题标题】:Mockito @InjectMocks not assigned a @Spy to the correct fieldMockito @InjectMocks 未将 @Spy 分配给正确的字段
【发布时间】:2017-12-12 09:49:35
【问题描述】:

我有一个简单的测试用例,其中包含一个被监视的模拟对象列表,然后我将其注入到正在测试的类中

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

我不希望 DemonstationList 在任何一种情况下都有任何值。根据我在@injectmocks 的文档中阅读的内容,它与 rulesService 的名称不同或类型相同。

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

【问题讨论】:

  • 您有什么理由必须监视该列表?模拟和设置还不够吗?
  • @MaciejKowalski 虽然这可能会导致更干净的测试,但它可能仍会导致相同的问题。
  • 如果使用 Mock,可以指定要注入的字段的名称
  • 如果您模拟列表,则无法向其中添加模拟对象。
  • 你可以设置它.. 只是更喧嚣那是真的

标签: java spring spring-boot junit mockito


【解决方案1】:

这里有两件事在起作用。首先,运行时不存在泛型,所以基本上 Mockito 会同时看到 List 实例,然后应该选择一个。第二个问题是你的字段被声明为final,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);
}

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

另一方面,使用 Mockito 2.x 时,它将优先使用与 spy/mock 相同的字段名称的字段,正如您在 the documentation 中看到的那样:

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

还有:

注意 1:如果你有相同类型(或相同擦除)的字段,最好用匹配的字段命名所有 @Mock 注释的字段,否则 Mockito 可能会混淆并且注入失败'不会发生。

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

同样代表设置器或字段,它们可以以私有可见性声明,Mockito 将通过反射看到它们。 但是静态或最终的字段将被忽略

(重点是我自己的)

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

【讨论】:

  • 我已删除 final 但在使用构造函数注入时它仍会填充两个列表。在这个测试用例中,第二个列表“deomonstrationList”根本不应该被嘲笑。
  • @RobertMason 如果您删除了 final 关键字并使用了字段注入会怎样?如果你的 spy 和你的实际字段都被命名为 ruleService,这现在应该可以工作了。
  • 在问题的顶部,您会看到,这是我的第一个实现,使用同名的字段注入。但由于某种原因,它似乎想将其设置为 DemonstationList
  • @RobertMason 的名字,我还不知道,但是请注意类型是相同的,都是List
  • @RobertMason 你说得对,我引用的文档来自 Mockito 2.x,而 Spring boot 1.x 默认使用 Mockito 1.x。但是,如果您将 mockito-core 版本更改为 2.x,它应该可以像我提到的那样工作。我现在在回答中澄清了这一点。
【解决方案2】:

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

测试

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

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2015-07-09
    • 1970-01-01
    • 1970-01-01
    • 2015-04-02
    • 1970-01-01
    相关资源
    最近更新 更多