尽管创建了 2 个不同的模拟,但 InjectMocks 错误地将相同的 Mock 注入到 2 个相似类型的不同字段中

InjectMocks is wrongly injecting the same Mock into 2 different fields of similar type despite creating 2 different mocks

我有一个 class,它有 2 个相似类型的字段。我嘲笑过他们两个。但是当我使用 InjectMocks 时,注入模拟错误地将单个模拟注入到这两个字段中。

这里是示例代码class:


import lombok.AccessLevel;
import lombok.RequiredArgsConstructor;

import java.util.Set;
import java.util.function.Consumer;

@RequiredArgsConstructor(access = AccessLevel.PRIVATE)
public class TestClass {

    private final Consumer<Set<Integer>> intConsumer;

    private final Consumer<Set<String>> stringConsumer;

    void PrintClass(){
        System.out.println("intConsumers: " + intConsumer);
        System.out.println("stringConsumers: " + stringConsumer);
    }
}

这里是测试class:


import org.junit.Test;
import org.junit.runner.RunWith;
import org.mockito.InjectMocks;
import org.mockito.Mock;
import org.mockito.junit.MockitoJUnitRunner;

import java.util.Set;
import java.util.function.Consumer;

@RunWith(MockitoJUnitRunner.class)
public class TestClassTest {

    @Mock private Consumer<Set<Integer>> intConsumer;
    @Mock private Consumer<Set<String>> stringConsumer;
    @InjectMocks private TestClass testClass;

    @Test
    public void testPrint(){
        testClass.PrintClass();
    }


}

这是我 运行 测试时的输出:testPrint() - intConsumer 被注入到 intConsumer 和 stringConsumer 中。

intConsumers: intConsumer
stringConsumers: intConsumer



Process finished with exit code 0

我正在使用 Maven。

<dependency>
        <groupId>org.mockito</groupId>
        <artifactId>mockito-core</artifactId>
        <version>2.7.19</version>
</dependency>

我创建了这个私有构造函数,专门用于使用 InjectMocks 进行测试。我不想让它 public/package-private 所以我不能使用字段注入。我也不想使用 public 设置器公开这些字段。另外,我不想让我的字段成为非最终字段。

我试过将 mockito-version 升级到 3.5.10 但它仍然有这个错误。 我也尝试过使我的字段最终化并使用 setter - 然后注入工作正常 - 但我不想公开我的 setter。 我也尝试过使用构造函数注入来命名模拟 @Mock(name = "mock"),但效果不佳。

我是不是漏掉了什么?有没有办法让它与私有构造函数注入一起工作?

这是 Mockito 中的一个未解决的错误。

From what I see, PropertyAndSetterInjection takes generic types and @Mock's name attributes into account, so it works as expected for the field injection. But it doesn't work with constructors because ConstructorInjection is using only SimpleArgumentResolver which is well... very simple and doesn't have any MockCandidateFilter like property injector does.

通常,您会:

  • 删除 @InjectMocks 并在测试的设置方法中构造 TestClass 实例。恕我直言,这是侵入性较小的方法。
  • 或者,如上所述,字段和 setter 注入工作。

这两种方法都不适用于您的约束(私有构造函数,无 setters,final 字段)。

在这种情况下,您可以借助反射来构建实例:

@Before
public void setUp() throws IllegalAccessException, 
        InvocationTargetException, 
        InstantiationException,
        NoSuchMethodException {
    final Constructor<TestClass> constructor = TestClass.class.getDeclaredConstructor(Consumer.class, Consumer.class);
    constructor.setAccessible(true);
    testClass = constructor.newInstance(intConsumer, stringConsumer);
}