
mockito
使用Mockito进行Java类的模拟和存根的任何人,可能都熟悉InjectMocks -annotation。 在要测试的类上使用此批注,Mockito将尝试通过构造函数注入,setter注入或属性注入来注入模拟。 该魔术成功,无提示失败或抛出MockitoException
。
我想解释什么原因导致“ MockitoException:无法实例化名为xxx的@InjectMocks字段! 原因:类型是接口”以及解决方法。
问题
考虑以下JUnit 5测试,该测试可以验证女服务员是否可以正确提供早餐。 厨房工作人员中的任何人都可以提供早餐,并且该测试会验证当提供早餐时,咖啡机开始冲泡咖啡,而烤面包机开始烘烤。
@ExtendWith (MockitoExtension. class )
public class WaitressTest {
@Mock
CoffeeMachine coffeeMachine;
@Mock
Toaster toaster;
@InjectMocks
KitchenStaff waitress;
@Test
void should_serve_breakfast() {
waitress.serve(BREAKFAST);
verify(coffeeMachine).brew();
verify(toaster).toast();
}
}
interface KitchenStaff {
void serve(MealType mealType);
}
Waitress class implements KitchenStaff {
CoffeeMachine coffeeMachine;
Toaster toaster;
//...
@Override
public void serve(MealType mealType) {
coffeeMachine.brew();
toaster.toast();
}
}
为了测试的目的,Mockito模拟了协作的coffeeMachine
和toaster
-因此它们需要Mock
注释-这样我们就可以验证是否调用了预期的方法。 女服务员是真的,她正在接受测试。 通过在她身上放置@InjectMocks
,Mockito创建一个实例并传入两个协作者-然后调用我们实际的@Test
-annotated方法。
不幸的是,它失败了:一旦运行测试,Mockito就会引发运行时异常: “无法实例化名为'waitress'的@InjectMocks字段! 原因:“ KitchenStaff”类型是接口。”
原因
幸运的是,最近Mockito的错误消息已得到改进,并且清楚地指出了问题所在: KitchenStaff
类型是一种接口。
- 我们有一个界面。
interface KitchenStaff { void serve(MealType mealType); }
- 我们对Mockito说:“实例化此接口”(什么?)
@InjectMocks KitchenStaff waitress
- 嘿,那不对!
您不能仅在接口上使用@InjectMocks
,因为Mockito需要知道要实例化的具体类。
请记住,您正在测试的单元是通常是真实的少数幸运之一。 KitchenStaff
只是一个行为合同, Waitress
实际上是得到报酬以提供早餐。
解
与使用抽象类一样,也有一些,但是归结为:在实例声明时提供具体的类型。
为Mockito提供实现该接口的类。
A)声明一个具体的类型
对@InjectMocks
字段的类型使用具体的实现。
@InjectMocks
Waitress waitress;
B)分配具体类型
继续对@InjectMocks
字段使用接口的类型,但是使用具体的实现对其进行初始化。
@InjectMocks
KitchenStaff waitress = new Waitress()
或者当然在声明和初始化中使用具体类型,当然也可以
然而…
但是,您的受测类是否期望(必需)协作者作为构造函数的参数?(我当然希望如此!)
例如,考虑以下单个构造函数:
Waitress class implements KitchenStaff {
final CoffeeMachine coffeeMachine;
final Toaster toaster;
Waitress(CoffeeMachine coffeeMachine, Toaster toaster) {
this .coffeeMachine = coffeeMachine;
this .toaster = toaster;
}
然后,在没有一个无参数的构造函数,编译器会告诉你调用适当的构造函数,并提供论据就在那里,现在。
@InjectMocks
KitchenStaff waitress = new Waitress()
^^
compiler error
A.通过自己提供参数来解决。
// compiles again
@InjectMocks
KitchenStaff waitress = new Waitress(coffeeMachine, toaster);
B.删除@InjectMocks
。 您不再需要它。
KitchenStaff waitress;
@BeforeEach
void setup() {
waitress = new Waitress(coffeeMachine, toaster);
}
结论
相信女服务员为您做些美味的面包。
mockito