Mockito 如何处理供应商?
How does Mockito handling Suppliers?
在第一个截图上你可以看到我的测试class。此 class 被注释为 @ExtendWith({MockitoExtension.class})
,并且被测试的服务也被注释为 @InjectMocks
。在第二个屏幕截图中,您可以看到经过测试的服务。
为什么 Mockito 在这两种情况下都使用 long 供应商?
Mockito 在按以下顺序注入模拟时使用不同的 strategies:
- 构造函数注入
- 属性setter注入
- 字段注入
字段注入 在您的示例中不起作用,因为服务的字段被声明为 final
.
由于声明了字段 final
并且您显示的代码片段没有字段初始值设定项,因此我假设您有一个带有 Supplier
参数的构造函数。例如
public SomeService(Supplier<String> stringSupplier, Supplier<Long> longTimeSupplier) {
this.stringSupplier = stringSupplier;
this.longTimeSupplier = longTimeSupplier;
}
因此 Mockito 将尝试 构造函数注入 ,找到具有两个 Supplier
参数的构造函数并尝试解析参数。
Mockito 然后在测试中找到两个 Supplier
mock,但由于类型擦除,它看不到通用类型。因此 Mockito 看到构造函数是这样的:
public SomeService(Supplier stringSupplier, Supplier longTimeSupplier)
Mockito 也无法根据参数名称决定使用哪个 Supplier
,因为正常的 Java 反射 API 不提供该信息。所以模拟的名称将不会被考虑在内。
像paranamer这样的库可以读取字节码并提取调试信息以读取参数名称,但 Mockito 不使用该库。
因此 Mockito 只注入第一个匹配的模拟,在你的情况下是 Supplier<String> stringSupplier
。即使您的问题与泛型有关,当您有两个非泛型的相同类型的参数时,Mockito 也会以相同的方式行事。
我假设你有一个构造函数接受两个 Supplier
。所以你可以在你的测试之前调用它。
@BeforeEach
public void setup() {
service = new SomeService(stringSupplier, longSupplier);
}
如果您无法访问构造函数,例如它具有 包范围 ,您需要使用反射调用它并将可访问的 属性 设置为 true
@BeforeEach
public void setup() throws Exception {
Constructor<SomeService> constructor = SomeService.class.getConstructor(Supplier.class, Supplier.class);
constructor.setAccessible(true);
service = constructor.newInstance(stringSupplier, longSupplier);
}
PS 如果要删除 final
,请确保模拟以服务中的字段命名 longTimeSupplier
与 longSupplier
或者您使用 @Mock(name = "longTimeSupplier")
.
在第一个截图上你可以看到我的测试class。此 class 被注释为 @ExtendWith({MockitoExtension.class})
,并且被测试的服务也被注释为 @InjectMocks
。在第二个屏幕截图中,您可以看到经过测试的服务。
为什么 Mockito 在这两种情况下都使用 long 供应商?
Mockito 在按以下顺序注入模拟时使用不同的 strategies:
- 构造函数注入
- 属性setter注入
- 字段注入
字段注入 在您的示例中不起作用,因为服务的字段被声明为 final
.
由于声明了字段 final
并且您显示的代码片段没有字段初始值设定项,因此我假设您有一个带有 Supplier
参数的构造函数。例如
public SomeService(Supplier<String> stringSupplier, Supplier<Long> longTimeSupplier) {
this.stringSupplier = stringSupplier;
this.longTimeSupplier = longTimeSupplier;
}
因此 Mockito 将尝试 构造函数注入 ,找到具有两个 Supplier
参数的构造函数并尝试解析参数。
Mockito 然后在测试中找到两个 Supplier
mock,但由于类型擦除,它看不到通用类型。因此 Mockito 看到构造函数是这样的:
public SomeService(Supplier stringSupplier, Supplier longTimeSupplier)
Mockito 也无法根据参数名称决定使用哪个 Supplier
,因为正常的 Java 反射 API 不提供该信息。所以模拟的名称将不会被考虑在内。
像paranamer这样的库可以读取字节码并提取调试信息以读取参数名称,但 Mockito 不使用该库。
因此 Mockito 只注入第一个匹配的模拟,在你的情况下是 Supplier<String> stringSupplier
。即使您的问题与泛型有关,当您有两个非泛型的相同类型的参数时,Mockito 也会以相同的方式行事。
我假设你有一个构造函数接受两个 Supplier
。所以你可以在你的测试之前调用它。
@BeforeEach
public void setup() {
service = new SomeService(stringSupplier, longSupplier);
}
如果您无法访问构造函数,例如它具有 包范围 ,您需要使用反射调用它并将可访问的 属性 设置为 true
@BeforeEach
public void setup() throws Exception {
Constructor<SomeService> constructor = SomeService.class.getConstructor(Supplier.class, Supplier.class);
constructor.setAccessible(true);
service = constructor.newInstance(stringSupplier, longSupplier);
}
PS 如果要删除 final
,请确保模拟以服务中的字段命名 longTimeSupplier
与 longSupplier
或者您使用 @Mock(name = "longTimeSupplier")
.